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
- Sign in to the platform console, create a project and copy its full UUID.
- Create a project API key, set its scopes and save the secret, which is shown only once.
- Create a cat identity, keep the returned id and upload enrollment images.
- 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:read | Read identities, image lists and image content |
cats:write | Manage identities, enroll images, set primary photos and select enrollment candidates |
recognition:write | Run cat identity searches |
tasks:read | Read 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 → queuedPoll 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 |
|---|
| 401 | invalid_api_key | Invalid format, secret, or unknown key |
| 401 | expired_api_key | Key has expired |
| 403 | api_key_disabled / api_key_revoked | Key is disabled or revoked |
| 403 | project_mismatch | Key belongs to another project |
| 403 | scope_denied | Required scope is missing |
| 403 | ip_not_allowed | Source IP is not allowed |
| 404 | Not found | Project, identity, image, or task not found |
| 409 | Conflict | Task state does not allow this operation |
| 413 | Image is too large | Upload exceeds the configured size limit |
| 415 | Unsupported media type | Only JPEG, PNG, and WebP are supported |
| 422 | Validation error | Check required fields, types, and ranges |
| 429 | rate_limit_exceeded | Reduce concurrency and respect Retry-After |
| 503 | Recognition task failed | Inference failed; check task state before retrying |
API REFERENCE / v1
Recognize a cat
POST/v1/projects/{project_id}/search
Required scope: recognition: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.
Query parameters and results
top_k is an integer from 1 to 100, default 5 (configurable); threshold is an optional similarity cutoff from −1 to 1. The model default is used when omitted.
matches contains candidates ranked by similarity; unknown=true means no reliable identity met the threshold. In candidates , score is the detection score, not identity similarity. Detecting a face does not mean identifying the cat.
{
"cat_count": 1,
"route": "face",
"face_detected": true,
"unknown": true,
"matches": [],
"candidates": [
{
"index": 0,
"score": 0.95,
"route": "face",
"face_detected": true,
"unknown": true,
"matches": []
}
],
"task_id": "<task_id>"
}Example response structure, not a live result.
curl --request POST '{{base_url}}/v1/projects/{project_id}/search?top_k=5' \
--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}/search?top_k=5",
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
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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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 --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.