The Relay API is organized around REST principles. It accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes.
Base URL
All API requests should be made to:
Authentication
Authenticate requests by including your API key in the X-API-Key header:
curl https://api.relayai.dev/api/v1/datasets \
-H "X-API-Key: your_api_key_here"
API keys are created in the Relay dashboard under Settings > API Keys.
Keep your API key secure. Do not share it in public repositories or client-side code.
- Content-Type:
application/json for all requests with a body
- Method: Use appropriate HTTP methods (GET, POST, PATCH, DELETE)
- IDs: All resource IDs are UUIDs
curl -X POST https://api.relayai.dev/api/v1/datasets \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Dataset", "artifact_types": [{"name": "glitch"}]}'
All responses are JSON-encoded. Successful responses include the requested resource or a confirmation.
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "My Dataset",
"artifact_types": [{"name": "glitch"}],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
HTTP status codes
| Code | Description |
|---|
200 | OK - Request succeeded |
201 | Created - Resource created successfully |
204 | No Content - Request succeeded (delete operations) |
400 | Bad Request - Invalid request format |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Valid key but insufficient permissions |
404 | Not Found - Resource doesn’t exist |
422 | Validation Error - Request body validation failed |
500 | Internal Server Error - Server-side error |
Error responses
Errors return a JSON object with a detail field:
{
"detail": "Dataset not found"
}
Validation errors (422) include details about which fields failed:
{
"detail": [
{
"loc": ["body", "name"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
List endpoints support pagination with page and page_size parameters:
curl "https://api.relayai.dev/api/v1/datasets/123/audio?page=1&page_size=50" \
-H "X-API-Key: $API_KEY"
Paginated responses include:
{
"items": [...],
"total": 150,
"page": 1,
"page_size": 50,
"has_more": true
}
| Field | Description |
|---|
items | Array of resources |
total | Total count across all pages |
page | Current page number (1-indexed) |
page_size | Items per page |
has_more | Whether more pages exist |
Idempotency
POST requests that create resources are not idempotent. To avoid duplicates, track the returned id from successful requests.
Versioning
The API version is included in the URL path: /api/v1/...
Breaking changes will be introduced in new versions (e.g., /api/v2/...). Existing versions remain supported.
Resource hierarchy
Tenant (your account)
├── Datasets
│ ├── Audio Files
│ └── Annotation Sets
│ └── Annotations
├── Training Jobs
├── Models
└── Inference Jobs
└── Inference Files
Common patterns
Presigned URL uploads
Audio file uploads use presigned URLs for direct-to-storage uploads:
- Request URL: POST to get a presigned upload URL
- Upload: POST the file to the presigned URL
- Confirm: POST to confirm the upload completed
This keeps large files off the API servers and enables faster uploads.
Async operations
Training and inference are async operations:
- Create: POST to create a job
- Poll: GET the job periodically to check status
- Results: Read results when status is
completed
while True:
job = get_job(job_id)
if job["status"] == "completed":
break
time.sleep(10)
Draft and publish
Annotation sets follow a draft → publish workflow:
- Create set (starts as draft)
- Add/edit annotations
- Publish (locks the set)
- Use for training
API endpoints