Skip to main content
You can call the Avala API directly over HTTP from any programming language, shell script, or automation tool. No SDK required.

Base URL

All API requests are made to:
https://api.avala.ai/api/v1

Authentication

Every request must include your API key in the X-Avala-Api-Key header.
X-Avala-Api-Key: your-api-key
You can find your API key in the Avala dashboard under Settings > Security.

Making Requests

Below are examples of listing datasets across several languages.
curl -X GET "https://api.avala.ai/api/v1/datasets/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" \
  -H "Content-Type: application/json"

Handling Pagination

List endpoints return paginated results using cursor-based pagination with next, previous, and results fields. Response structure:
{
  "next": "https://api.avala.ai/api/v1/datasets/?cursor=cD0yMDI0...",
  "previous": null,
  "results": [...]
}
FieldTypeDescription
nextstring | nullURL for the next page, or null if this is the last page
previousstring | nullURL for the previous page, or null if this is the first page
resultsarrayArray of resource objects for the current page
Paginating through all results:
# First page
curl -s "https://api.avala.ai/api/v1/datasets/?limit=50" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" | jq .

# Next page (use the full `next` URL from previous response)
curl -s "https://api.avala.ai/api/v1/datasets/?cursor=cD0yMDI0&limit=50" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" | jq .

Error Handling

The API returns standard HTTP status codes. Error responses include a JSON body with details. Error response format:
{
  "detail": "Not found."
}
For validation errors, DRF returns field-level errors:
{
  "field_name": ["This field is required."]
}
Status CodeMeaning
200Success
201Created
400Bad request — check the detail field or field-level errors in the response body.
401Unauthorized — invalid or missing API key.
404Not found — the resource does not exist.
429Rate limited — too many requests. Respect the Retry-After header.
500Internal server error — try again later.

Handling Errors in Code

import requests
import os
import time

headers = {"X-Avala-Api-Key": os.environ["AVALA_API_KEY"]}

response = requests.get(
    "https://api.avala.ai/api/v1/datasets/nonexistent/",
    headers=headers,
)

if response.status_code == 200:
    data = response.json()
elif response.status_code == 401:
    print("Invalid API key. Check your AVALA_API_KEY.")
elif response.status_code == 404:
    print("Resource not found.")
elif response.status_code == 429:
    retry_after = int(response.headers.get("Retry-After", 60))
    print(f"Rate limited. Retrying in {retry_after}s...")
    time.sleep(retry_after)
else:
    print(f"Error {response.status_code}: {response.json().get('detail', 'Unknown error')}")
When you receive a 429 response, always check the Retry-After header for the number of seconds to wait before retrying. The X-RateLimit-Remaining header on every response tells you how many requests you have left.

File Uploads

File uploads use presigned URLs rather than direct multipart uploads to the API. The general flow is:
  1. Request a presigned upload URL from the API
  2. Upload the file directly to cloud storage using the presigned URL
  3. Confirm the upload with the API
# Step 1: Request presigned URL
UPLOAD_INFO=$(curl -s -X POST "https://api.avala.ai/api/v1/datasets/manual-upload/file-upload-url/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_path_in_dataset": "image001.jpg", "dataset_name": "my-dataset"}')

PRESIGNED_URL=$(echo $UPLOAD_INFO | jq -r '.url')

# Step 2: Upload to cloud storage using the presigned POST fields
# All fields from the response must be included as form data
curl -X POST "$PRESIGNED_URL" \
  $(echo $UPLOAD_INFO | jq -r '.fields | to_entries[] | "-F \(.key)=\(.value)"') \
  -F "file=@./image001.jpg"

Common Patterns

List All Items with Pagination

This pattern fetches every item in a dataset by following pagination URLs until all pages have been retrieved.
import requests
import os

base_url = "https://api.avala.ai/api/v1"
headers = {"X-Avala-Api-Key": os.environ["AVALA_API_KEY"]}

def get_all_items(owner, slug):
    items = []
    url = f"{base_url}/datasets/{owner}/{slug}/items/?limit=100"

    while url:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        data = response.json()

        items.extend(data["results"])
        url = data.get("next")

    return items

all_items = get_all_items("acme-ai", "training-v2")
print(f"Total items: {len(all_items)}")

Create and Wait for an Export

This pattern starts an export job and polls until it finishes, then retrieves the download URL.
# Start the export
EXPORT=$(curl -s -X POST "https://api.avala.ai/api/v1/exports/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"project_id": "project_abc123"}')

EXPORT_UID=$(echo $EXPORT | jq -r '.uid')
echo "Export started: $EXPORT_UID"

# Poll until completed
while true; do
  STATUS=$(curl -s "https://api.avala.ai/api/v1/exports/$EXPORT_UID/" \
    -H "X-Avala-Api-Key: $AVALA_API_KEY" | jq -r '.status')

  echo "Status: $STATUS"

  if [ "$STATUS" = "completed" ]; then
    break
  fi

  sleep 2
done

# Get the download URL
DOWNLOAD_URL=$(curl -s "https://api.avala.ai/api/v1/exports/$EXPORT_UID/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" | jq -r '.download_url')

echo "Download: $DOWNLOAD_URL"