DEVELOPERS / OPENAPI v1

Give your app
a sense of identity.

Maopu cat recognition API guide

From your first enrollment image to an identity search. Use standard HTTP endpoints to build cat registries, manage tasks and retrieve identity candidates.

Public documentation · No login required

Before your first request

  1. Sign in to the platform console, create a project and copy its full UUID.
  2. Create a project API key, set its scopes and save the secret, which is shown only once.
  3. Create a cat identity, keep the returned id and upload enrollment images.
  4. Check the task list. Once enrollment completes, upload a query image to get ranked matches.
In these examples, {{base_url}}is your HTTPS API service address, and {{api_key}} is your project key. Obtain the API address from your deployment settings or service provider. This website hosts documentation, not /v1 business requests.

cURL examples use Bash line continuations. Windows users can import them into Postman with Import → Raw text, set base_url and api_key, and reselect upload files. Run Python examples on your server with requests installed and the environment variables configured.

AUTHENTICATION

Project-scoped credentials

Business endpoints accept Authorization: Bearer mk_live_… or the project owner's login token. Each key is bound to a project and can access project data allowed by its scopes.

Scope Allowed operations
cats:readRead identities, image lists and image content
cats:writeManage identities, enroll images, set primary photos and select enrollment candidates
recognition:writeRun cat identity searches
tasks:readRead tasks, results, summaries and preview images

Key status, revocation, expiry and IP allowlists are checked during authentication. Full access includes all four scopes; an empty scopes array at key creation grants all four. Task retry currently accepts either cats:write or recognition:write.

Project creation and settings, key management, wallets, billing and usage queries require a login token rather than an API key. Store secrets in server-side environment variables.

WORKFLOW

Enroll asynchronously, query for results

Enrollment returns HTTP 202 with task_id. vectors_added: 0 is expected at this stage. Images become searchable after background feature extraction. Recognition requests are prioritized and wait for inference before returning results.

queued → running → completed
                 → waiting_user → selection → queued
                 → failed → retry → queued

Poll GET /tasks?limit=10&offset=0 every few seconds and find task_id by id. There is no standalone GET /tasks/ {task_id} detail endpoint. Tasks are ordered by creation time descending. Paginate with offset and deduplicate by task ID.

Multiple cats in an enrollment image may result in waiting_user. Inspect the candidate images and submit candidate_index through selection, or use cancel to abandon the pending enrollment. Only failed tasks can be retried.

A request timeout does not imply task failure. Check the task list before uploading again to avoid duplicate work and charges. These endpoints do not provide general Idempotency-Key deduplication.

LIMITS & ERRORS

Limits, billing and error handling

Default per-key limits per second are 2 recognition requests, 10 enrollment/write requests and 20 reads. Deployment settings may override them. On 429, respect Retry-After. Normal successful JSON responses expose X-RateLimit-Limit and X-RateLimit-Remaining.

The configurable default image limit is 15 MiB. JPEG, PNG and WebP are supported. Enrollment and recognition charge for cat embedding extraction; consult the console for current prices and grants. Error detail can be a string, structured object or validation array. Keep the HTTP status and X-Request-Id.

HTTP Error code / detail Description
401invalid_api_keyInvalid format, secret, or unknown key
401expired_api_keyKey has expired
403api_key_disabled / api_key_revokedKey is disabled or revoked
403project_mismatchKey belongs to another project
403scope_deniedRequired scope is missing
403ip_not_allowedSource IP is not allowed
404Not foundProject, identity, image, or task not found
409ConflictTask state does not allow this operation
413Image is too largeUpload exceeds the configured size limit
415Unsupported media typeOnly JPEG, PNG, and WebP are supported
422Validation errorCheck required fields, types, and ranges
429rate_limit_exceededReduce concurrency and respect Retry-After
503Recognition task failedInference failed; check task state before retrying

API REFERENCE / v1

Create an identity

POST/v1/projects/{project_id}/cats

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

name is 1–100 characters and required at creation; metadata is a JSON object; active defaults to true. Send only the fields you want to update.

cURL / Postman

curl --request POST '{{base_url}}/v1/projects/{project_id}/cats' \
  --header 'Authorization: Bearer {{api_key}}' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Mimi","metadata":{},"active":true}'
View Python example
import os
import json
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.post(
    base_url + "/v1/projects/{project_id}/cats",
    headers={"Authorization": f"Bearer {api_key}"},
    json=json.loads("{\"name\":\"Mimi\",\"metadata\":{},\"active\":true}"),
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

List identities

GET/v1/projects/{project_id}/cats

Required scope: cats:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/cats' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/cats",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Update an identity

PATCH/v1/projects/{project_id}/cats/{cat_id}

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

name is 1–100 characters and required at creation; metadata is a JSON object; active defaults to true. Send only the fields you want to update.

cURL / Postman

curl --request PATCH '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}' \
  --header 'Authorization: Bearer {{api_key}}' \
  --header 'Content-Type: application/json' \
  --data '{"name":"Mimi","active":true}'
View Python example
import os
import json
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.patch(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}",
    headers={"Authorization": f"Bearer {api_key}"},
    json=json.loads("{\"name\":\"Mimi\",\"active\":true}"),
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Delete an identity

DELETE/v1/projects/{project_id}/cats/{cat_id}

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Deleting an identity removes its images and vectors; deleting an image removes its corresponding vectors. Identity deletion returns 204 with no body.

cURL / Postman

curl --request DELETE '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.delete(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Enroll an image

POST/v1/projects/{project_id}/cats/{cat_id}/images

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Use multipart/form-data with the required file field image . Upload one JPEG, PNG or WebP per request. Let the client generate Content-Type and boundary.

Returns 202 queued with task_id. Track enrollment in the task list.

cURL / Postman

curl --request POST '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}/images' \
  --header 'Authorization: Bearer {{api_key}}' \
  --form 'image=@cat.jpg;type=image/jpeg'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
with open("cat.jpg", "rb") as image:
    response = requests.post(
        base_url + "/v1/projects/{project_id}/cats/{cat_id}/images",
        headers={"Authorization": f"Bearer {api_key}"},
        files={"image": ("cat.jpg", image, "image/jpeg")},
        timeout=120,
    )
response.raise_for_status()
print(response.content)

API REFERENCE / v1

List enrolled images

GET/v1/projects/{project_id}/cats/{cat_id}/images

Required scope: cats:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}/images' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}/images",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Download an image

GET/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/content

Required scope: cats:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

The success response is binary image content, not JSON.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/content' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/content",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Set primary image

PATCH/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/primary

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

cURL / Postman

curl --request PATCH '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/primary' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.patch(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}/primary",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Delete an image

DELETE/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Deleting an identity removes its images and vectors; deleting an image removes its corresponding vectors. Identity deletion returns 204 with no body.

cURL / Postman

curl --request DELETE '{{base_url}}/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.delete(
    base_url + "/v1/projects/{project_id}/cats/{cat_id}/images/{image_id}",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

List tasks and results

GET/v1/projects/{project_id}/tasks

Required scope: tasks:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

limit ranges from 1 to 200, default 100; offset is at least 0, default 0. The returned array contains task statuses and result.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/tasks?limit=10&offset=0' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/tasks?limit=10&offset=0",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Task summary

GET/v1/projects/{project_id}/tasks/summary

Required scope: tasks:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/tasks/summary' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/tasks/summary",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Download query image

GET/v1/projects/{project_id}/tasks/{task_id}/query

Required scope: tasks:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

The success response is binary image content, not JSON.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/tasks/{task_id}/query' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/tasks/{task_id}/query",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Download candidate image

GET/v1/projects/{project_id}/tasks/{task_id}/candidates/{candidate_index}

Required scope: tasks:read. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

The success response is binary image content, not JSON.

cURL / Postman

curl --request GET '{{base_url}}/v1/projects/{project_id}/tasks/{task_id}/candidates/{candidate_index}' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.get(
    base_url + "/v1/projects/{project_id}/tasks/{task_id}/candidates/{candidate_index}",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Select enrollment candidate

POST/v1/projects/{project_id}/tasks/{task_id}/selection

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Only available for enrollment tasks in waiting_user. Other states return 409. candidate_index is a nonnegative integer.

cURL / Postman

curl --request POST '{{base_url}}/v1/projects/{project_id}/tasks/{task_id}/selection' \
  --header 'Authorization: Bearer {{api_key}}' \
  --header 'Content-Type: application/json' \
  --data '{"candidate_index":0}'
View Python example
import os
import json
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.post(
    base_url + "/v1/projects/{project_id}/tasks/{task_id}/selection",
    headers={"Authorization": f"Bearer {api_key}"},
    json=json.loads("{\"candidate_index\":0}"),
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Abandon pending selection

POST/v1/projects/{project_id}/tasks/{task_id}/cancel

Required scope: cats:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Only available for enrollment tasks in waiting_user. Other states return 409. candidate_index is a nonnegative integer.

cURL / Postman

curl --request POST '{{base_url}}/v1/projects/{project_id}/tasks/{task_id}/cancel' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.post(
    base_url + "/v1/projects/{project_id}/tasks/{task_id}/cancel",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

API REFERENCE / v1

Retry a failed task

POST/v1/projects/{project_id}/tasks/{task_id}/retry

Required scope: cats:write / recognition:write. Also accepts the project owner's login token. Replace all path placeholders with actual IDs.

Only failed tasks with retained source data can be retried. Returns 202 on success.

cURL / Postman

curl --request POST '{{base_url}}/v1/projects/{project_id}/tasks/{task_id}/retry' \
  --header 'Authorization: Bearer {{api_key}}'
View Python example
import os
import requests

api_key = os.environ["MEOWID_API_KEY"]
base_url = os.environ["MEOWID_API_BASE"].rstrip("/")
# Replace {project_id}, {cat_id}, etc. with actual IDs.
response = requests.post(
    base_url + "/v1/projects/{project_id}/tasks/{task_id}/retry",
    headers={"Authorization": f"Bearer {api_key}"},
    timeout=120,
)
response.raise_for_status()
print(response.content)

Endpoint paths, request bodies and scopes are synchronized from the existing platform documentation. Examples contain placeholders only, never live credentials.