OwnClimb Detection API
Asynchronous climbing-hold detection. Submit a wall image, then poll for the run's result. The OpenAPI document is the machine-readable contract for the same reference.
Base URL: https://ownclimb.com/api/v1
Authentication
Send your oc_ API key as Authorization: Bearer <key>, or as the X-API-Key
header.
Quickstart
Submit an image:
curl -X POST https://ownclimb.com/api/v1/runs \
-H "Authorization: Bearer oc_your_key" \
-F "image=@wall.jpg" \
-F 'metadata={"gym":"north"}'
# → 202 { "id": "…", "status": "queued", … }
Poll for the result:
curl https://ownclimb.com/api/v1/runs/RUN_ID \
-H "Authorization: Bearer oc_your_key"
# → 200 { "status": "done", "result": { "holds": [ … ] } }
Endpoints
POST /api/v1/runs
Submit an image for hold detection.
Multipart form with an image file and optional JSON fields. Returns 202
immediately; the run is processed asynchronously.
| Field | Type | Required | Description |
|---|---|---|---|
image | file | yes | Wall photo (JPEG/PNG/WebP/HEIC, max 40 MB). |
config | string | no | Detection config id. Defaults to the standard config. |
metadata | JSON string | no | Optional object echoed back in run history. |
| Status | Meaning |
|---|---|
202 | Run accepted and queued. |
400 | invalid_request or invalid_image. |
401 | unauthorized. |
402 | quota_exceeded or account_locked. |
413 | image_too_large. |
429 | rate_limited; wait out the retry-after header. |
GET /api/v1/runs/{id}
Get a run.
| Path parameter | Required | Description |
|---|---|---|
id | yes | The run's uuid. |
| Status | Meaning |
|---|---|
200 | Current run state; result.holds is present once status is done. |
401 | unauthorized. |
404 | not_found. |
GET /api/v1/runs/{id}/progress
Stream a run's progressive stages.
Server-sent events (text/event-stream) that deliver the run's stages as they
finish: status with the current state, progress with the holds each stage
has found so far, then done with the final run or failed with the error.
The stream ends at the terminal event, or after two minutes if the worker never
finishes.
| Path parameter | Required | Description |
|---|---|---|
id | yes | The run's uuid. |
| Status | Meaning |
|---|---|
200 | The SSE stream. |
401 | unauthorized. |
404 | not_found. |
Error codes
Errors come back as { "error": { "code", "message" } }, with an optional
details object on validation failures.
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The request or its fields are not valid. |
invalid_image | 400 | The upload does not decode as an image. |
image_too_large | 413 | The image exceeds the size limit. |
unauthorized | 401 | Missing or invalid API key. |
not_found | 404 | No such run, or it belongs to another account. |
quota_exceeded | 402 | The monthly inference quota is used up. |
account_locked | 402 | The subscription is inactive. |
rate_limited | 429 | Too many requests; retry after the delay. |
inference_failed | 500 | The detection run failed. |
storage_unavailable | 503 | Image storage was unreachable. |