apiai.me Docs

API Documentation

Everything you need to integrate apiai.me into your application.

Overview

apiai.me gives you a library of pre-built Tools — ready-to-use API endpoints for image generation, editing, background removal, video creation, and more. The workflow is always the same: configure and test in the Dashboard, then copy the generated curl command into your code. No manual request-building required.

You can also chain tools into Pipelines — the Dashboard generates a single curl for the whole chain, just like it does for individual tools.

All API responses are JSON unless the endpoint returns binary data (images, video). Errors follow a consistent format:

Error Response
{ "error": "description of what went wrong" }

How It Works

1. Browse the Toolbox

Log in to the Dashboard and open the API Toolbox. You'll see every tool available to your account — each with its parameters, defaults, options, and a ready-made curl command.

2. Configure & test in the playground

Expand any tool to see its full parameter form. Adjust values, upload an image, and hit Run to test it live — right in the browser. The curl command updates in real time as you change parameters, so when you're happy with the result you have a ready-to-run command that exactly matches what you tested.

3. Copy the curl — paste into your project

Hit Copy next to the curl command. The command already has your API key, endpoint, and the parameter values from the playground baked in. Paste it directly into your codebase or use it as the basis for any HTTP client call.

What you get after clicking Copy
curl -X POST https://apiai.me/api/process/remove-bg \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo.png" \
  --output result.png

4. Chain into a Pipeline (optional)

In My Pipelines, connect tools in a node graph and publish. The Dashboard gives you a single curl for the whole pipeline — copy and use it exactly like a single-tool call. Enterprise accounts can also receive Custom Pipelines pre-built by our team, available in the Custom Pipelines section with their own ready-made curl.

Authentication

Sign up at apiai.me to create your account. Authentication is passwordless — you receive a one-time code by email, verify it, and receive a session token for the dashboard. Your api_key is shown once at registration; use it for all programmatic API calls.

Step 1 — Request a login code

POST /api/login

Request Body

FieldTypeDescription
email requiredstringThe email associated with your account
Example
curl -X POST https://apiai.me/api/login \
  -H "Content-Type: application/json" \
  -d '{"email": "you@company.com"}'
Response 200
{ "requires_verification": true, "email": "you@company.com" }

A 6-digit one-time code is emailed to you. Codes expire after 10 minutes.

Step 2 — Verify the code and get your session

POST /api/verify

Request Body

FieldTypeDescription
email requiredstringSame email used in the login request
code requiredstringThe 6-digit code from the email
Example
curl -X POST https://apiai.me/api/verify \
  -H "Content-Type: application/json" \
  -d '{"email": "you@company.com", "code": "482916"}'
Response 200
{ "session_token": "sess_...", "username": "you", "email": "you@company.com" }

Your API key (ak_...) is provided once at registration. Store it securely — it cannot be retrieved later. You can regenerate a new key from the Dashboard at any time.

Step 3 — Use your API key

Pass your API key in the X-API-Key header on every protected request.

Header
X-API-Key: ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

If your key is compromised, you can regenerate it from the Dashboard — the old key is immediately invalidated.

Rate Limits

To protect service stability, the API enforces the following limits:

LimitScopeValue
Concurrent execution requestsPer user2 in-flight
Concurrent execution requestsServer-wide2 in-flight
Concurrent lightweight requests (reads, dashboard)Server-wide10 in-flight
Request body sizePer request50 MB (recommended < 20 MB — larger images are auto-downscaled to ~2048 px before processing; 200 MB for batch uploads)

Execution requests (everything that runs a tool, flow, pipeline, or batch item) share a server-wide cap of 2 in-flight jobs to protect GPU/CPU resources. Lightweight reads (listing tools, fetching balance, etc.) share a separate cap of 10.

When you exceed the per-user concurrency limit, the API returns 429 Too Many Requests:

Response 429
{ "error": "Too many concurrent requests. Maximum 2 in-flight requests per user." }

When the server is at full capacity, the API returns 503 Service Unavailable:

Response 503
{ "error": "Server at capacity. Maximum 2 concurrent processing jobs. Please retry shortly." }

Wait for an in-flight request to complete before sending the next one, or use the Batch API for high-volume workloads — batched items are queued server-side and don't count against the concurrency limit.

Base URL

https://apiai.me/api

All endpoint paths below are relative to this base URL.

List Tools

GET /workflows

List all tools available to your account. Each tool has a slug used in the process endpoint.

Example
curl https://apiai.me/api/workflows \
  -H "X-API-Key: ak_xxxx"
Response 200
[
  {
    "id": 7,
    "slug": "greyscale",
    "name": "Greyscale",
    "description": "Convert image to greyscale",
    "endpoint": "/api/process/greyscale",
    "method": "POST",
    "type": "workflow",
    "accepted_inputs": ["image"],
    "required_inputs": ["image"],
    "response_type": "image",
    "output_types": ["image"],
    "params": [
      {
        "name": "style",
        "expose_name": "style",
        "description": "Visual style to apply",
        "default_value": "natural",
        "allowed_values": ["natural", "vivid"],
        "required": false,
        "is_image": false
      }
    ]
  },
  {
    "id": 3,
    "slug": "logo-digitalize",
    "name": "Logo Digitalize",
    "description": "Multi-step logo pipeline",
    "endpoint": "/api/pipeline/logo-digitalize",
    "method": "POST",
    "type": "pipeline",
    "params": []
  }
]

The list includes both individual tools (type: "workflow") and admin-configured pipelines (type: "pipeline"). Use the endpoint field to determine the correct URL to call.

accepted_inputs lists which input types the tool understands (e.g. image, text, video). required_inputs is the subset of those that are mandatory — anything in accepted_inputs but not in required_inputs is optional.

Run a Tool

POST /process/{slug}

Run a tool. Accepts multipart/form-data. The response type depends on the tool — image, video, or JSON text (check output_types in the tool list).

Slugs and parameters are unique to your account. The tools available on your account, their slugs, and their parameter names are configured per-account. Discover yours via GET /api/workflows or from the parameter form for each tool in the Dashboard. Examples below use your-tool-slug and your-param as placeholders — replace them with values from your tool list.

Tips for the curl examples below

  • Add --fail-with-body so curl exits non-zero on errors instead of writing the JSON error into result.png.
  • For prompts with quotes, commas, or other special characters, single-quote the value: -F 'prompt=she said "go"'.
  • ak_xxxx is a placeholder — paste your real key (starts with ak_) from the Dashboard.

Parameters

FieldTypeDescription
image optionalfile / file[]Input image (PNG, JPG, WebP). Required for image-editing tools; optional or unused for generation tools. Some tools accept multiple images — check max_images in the tool list.
prompt optionalstringText prompt — required for generation tools, optional for editing tools.
output_filename optionalstringOverride the filename returned in Content-Disposition. Pass the stem (e.g. SKU-12345) — the extension is set from the actual response type. Useful for e-commerce / DAM workflows where output naming matters. If omitted, the original upload filename is preserved (so SKU-12345.png uploaded to a JPEG-output tool comes back as SKU-12345.jpg).
{param} optionalstringAny tool-specific parameter. The expose_name field in the tool's params array is the name to use; allowed_values lists valid options when present. Discover per-tool params via GET /api/workflows or from the Dashboard parameter form.

Response types

response_typeContent-TypeSave with
image (default)image/png, image/jpeg, etc.--output result.png
videovideo/mp4--output result.mp4
textapplication/json--output result.json
Image editing tool (image input)
curl -X POST https://apiai.me/api/process/your-tool-slug \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo.png" \
  --output result.png
Text-to-image tool (prompt only)
curl -X POST https://apiai.me/api/process/your-tool-slug \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=a minimalist tech logo" \
  --output result.png
Tool with custom parameters
# Each tool exposes its own parameters. Get the exact param names
# (the "expose_name" field) from GET /api/workflows or the Dashboard.
curl -X POST https://apiai.me/api/process/your-tool-slug \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=aerial view of a coral reef" \
  -F "your-param=value" \
  -F "another-param=value" \
  --output result.mp4
Multi-image input (tools with max_images > 1)
curl -X POST https://apiai.me/api/process/your-tool-slug \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo1.png" \
  -F "image=@photo2.png" \
  -F "prompt=combine these images" \
  --output result.png

Streaming progress (long-running tools)

For Replicate-backed tools that can take minutes (video generation, complex image models), append /stream to get real-time progress updates as a Server-Sent Events (SSE) stream. When the job finishes, pick up the result from GET /api/result/:id using the result_id in the final event.

SSE event typeMeaning
createdPrediction submitted to Replicate
startingReplicate is booting the model
processingModel is actively generating (sent on each poll)
doneJob complete — contains result_id and content_type
errorJob failed — contains message
Stream a long-running job
# Step 1 – submit and stream progress (-N disables curl buffering)
curl -N -X POST https://apiai.me/api/process/your-tool-slug/stream \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=aerial view of a coral reef"

# Each SSE line looks like:
# data: {"type":"starting","elapsed_ms":0}
# data: {"type":"processing","elapsed_ms":4200}
# data: {"type":"done","elapsed_ms":47300,"result_id":"a3f9...","content_type":"video/mp4"}

# Step 2 – download the result using the result_id from the done event
curl https://apiai.me/api/result/a3f9... \
  -H "X-API-Key: ak_xxxx" \
  --output result.mp4

Results are stored for 10 minutes and can only be retrieved once. Streaming requires the tool to be a Replicate workflow; other server types should use the synchronous endpoint.

Check supports_prompt and output_types in the tool list to know what each tool expects and returns.

Response headers:

HeaderDescription
X-Request-IDUnique request identifier (UUID)
X-Processing-TimeServer-side processing time in ms
X-CostCost charged for this request in USD
X-Balance-RemainingYour account balance after this request
X-WorkflowThe workflow or pipeline slug that was executed
X-Pipeline-StepsNumber of steps (only for multi-step pipelines)
POST /api/process/{slug}/estimate

Preview the cost of a request before running it. Same params shape as the real call but no provider is invoked, no balance is consumed.

Most tools have a fixed price per request — for those, the estimate just returns that price. Some tools are metered: the cost depends on params you submit (e.g. video duration, resolution) and/or on actual GPU time reported by the provider after the run completes. Use this endpoint to size the upper bound before submitting.

Get a cost estimate
curl -X POST https://apiai.me/api/process/your-tool-slug/estimate \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"params": {"duration_sec": 8, "resolution": "1080p"}}'

# Response (formula-priced tool):
# {
#   "slug": "your-tool-slug",
#   "kind": "formula",
#   "estimate": 8.10,
#   "min": 0.10,
#   "max": 20.00,
#   "reconciled": false,
#   "note": "Cost varies with the params you submit. Actual charge is fixed at request time."
# }

When reconciled is true, the upfront estimate is what we'll reserve; the final charge is based on actual provider metrics (e.g. GPU seconds) and is bounded by min/max. The same fields are surfaced in the tool list as pricing_kind, pricing_min, pricing_max, pricing_reconciled.

Pipelines

A pipeline chains multiple tools into a single API call. There are two kinds:

  • My Pipelines — pipelines you build yourself in the Dashboard node builder. Call via POST /api/flow/{slug}.
  • Custom Pipelines — pipelines built for your account by our team. They appear in the Custom Pipelines section of the Dashboard with their own curl command and playground. Call via POST /api/pipeline/{slug}.

To build your own: open the Dashboard, go to My Pipelines, and click New Pipeline. The pipeline gets a slug you can call immediately from the API or from Batch.

Pipelines can go further — the example below chains a format converter, upscaler, background remover, and image enhancer to turn a raw in-store photo into a production-ready product image in a single API call.

POST /pipeline/{slug}

Execute a multi-step pipeline. Pipelines chain multiple tools together — the output of each step feeds into the next.

Parameters

FieldTypeDescription
image requiredfileThe input image
Example
curl -X POST https://apiai.me/api/pipeline/logo-digitalize \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@logo.png" \
  --output result.png

Batch Processing

Process multiple images through a single workflow in one request. The server queues all items, processes them in parallel, and provides real-time progress via SSE. Results are available as a ZIP download for 1 hour after completion; multiple downloads are allowed within that window.

POST /batch

Create a new batch job. Upload multiple images and specify a workflow slug (or flow:slug for a user pipeline). All images will be processed with the same workflow/pipeline and parameters.

Parameters

FieldTypeDescription
workflow requiredstringWorkflow slug (e.g. remove-bg), user pipeline prefixed with flow: (e.g. flow:my-pipeline), or admin pipeline prefixed with pipeline: (e.g. pipeline:logo-digitalize)
images requiredfile[]Multiple image files (max limit set by server, default 50)
any paramstringAdditional workflow/pipeline-specific parameters (applied to all images)
Example — workflow
curl -X POST https://apiai.me/api/batch \
  -H "X-API-Key: ak_xxxx" \
  -F "workflow=remove-bg" \
  -F "images=@photo1.png" \
  -F "images=@photo2.png" \
  -F "images=@photo3.png"
Example — user pipeline
curl -X POST https://apiai.me/api/batch \
  -H "X-API-Key: ak_xxxx" \
  -F "workflow=flow:my-pipeline" \
  -F "images=@photo1.png" \
  -F "images=@photo2.png"

Response: 201 Created with job ID, status, total items, and estimated cost.

GET /batch

List your batch jobs (most recent first, max 50).

Example
curl https://apiai.me/api/batch \
  -H "X-API-Key: ak_xxxx"
GET /batch/{id}

Get batch job status and all item details.

Example
curl https://apiai.me/api/batch/42 \
  -H "X-API-Key: ak_xxxx"
POST /batch/{id}/cancel

Cancel a running batch job. Items already completed are kept; remaining items are skipped.

Example
curl -X POST https://apiai.me/api/batch/42/cancel \
  -H "X-API-Key: ak_xxxx"
GET /batch/{id}/download

Download all completed results as a ZIP archive. Available for 1 hour after job completion; multiple downloads are allowed within that window.

Example
curl https://apiai.me/api/batch/42/download \
  -H "X-API-Key: ak_xxxx" \
  --output results.zip

Batch progress is also streamed via SSE Events with event types batch_created and batch_progress.

Usage

GET /usage

Get aggregated usage statistics for your account.

Example
curl https://apiai.me/api/usage \
  -H "X-API-Key: ak_xxxx"

Logs

GET /logs

Retrieve recent request logs for your account.

Query Parameters

ParamTypeDescription
limit optionalintNumber of logs to return (default 50, max 200)
Example
curl https://apiai.me/api/logs?limit=20 \
  -H "X-API-Key: ak_xxxx"

SSE Events

GET /events

Server-Sent Events stream. Pushes real-time updates (new logs, usage changes) to connected clients. Authentication is via the X-API-Key header.

The browser EventSource API does not support custom headers — pass the key as a query param instead. For non-browser clients, use the header or fetch().

Browser (EventSource)
const es = new EventSource('/api/events?key=ak_xxxx');
es.addEventListener('usage', () => { /* reload stats */ });
es.addEventListener('balance_update', e => console.log(JSON.parse(e.data)));
Server / Node.js (fetch)
const resp = await fetch('/api/events', {
  headers: { 'X-API-Key': 'ak_xxxx' }
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(decoder.decode(value));
}

Billing

Payments are processed securely via Stripe. Each API call deducts from your account balance. When your balance is insufficient you'll receive a 402 Payment Required response.

GET /account/balance

Returns your current account balance.

Example
curl https://apiai.me/api/account/balance \
  -H "X-API-Key: ak_xxxx"
Response
{ "balance": 47.50 }
POST /account/create-checkout

Create a Stripe Checkout session to top up your balance ($10, $50, or $100). Returns a URL to redirect to for payment. Your card is saved for future auto-refill charges.

Body Parameters (JSON)

ParamTypeDescription
amount requirednumberTop-up amount: 10, 50, or 100
Example
curl -X POST https://apiai.me/api/account/create-checkout \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount": 50}'
Response
{ "url": "https://checkout.stripe.com/c/pay/..." }
GET /account/auto-refill

Get your auto-refill settings. When enabled, your saved card is charged automatically when your balance drops below $5.

Response
{ "enabled": true, "amount": 50 }
PUT /account/auto-refill

Enable or disable auto-refill. Requires a saved payment method (make at least one Stripe checkout payment first).

Body Parameters (JSON)

ParamTypeDescription
enabled requiredboolTurn auto-refill on or off
amount requirednumberAmount to charge: 10, 50, or 100
Example
curl -X PUT https://apiai.me/api/account/auto-refill \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "amount": 50}'
POST /account/regenerate-key

Rotate your API key. Returns a new key and immediately invalidates the old one. Use this if your key is leaked or you need to rotate credentials. The call itself must be authenticated with the current key.

Example
curl -X POST https://apiai.me/api/account/regenerate-key \
  -H "X-API-Key: ak_xxxx"
Response
{ "api_key": "ak_yyyy..." }

List Pipelines

GET /flows

List your pipelines (user-created tool chains). Each object includes slug, name, is_node_based, supports_prompt, first_step_requires_image, response_type, price_per_request, params (exposed parameters), flow_config (node graph, if node-based), and io_warnings (step compatibility warnings).

Example
curl https://apiai.me/api/flows \
  -H "X-API-Key: ak_xxxx"

Create Pipeline

POST /flows

Create a custom pipeline by chaining existing tools. You must have access to all referenced tools. Optionally provide a flow_config for node-based pipelines with clean parameter names.

Request Body

FieldTypeDescription
name requiredstringDisplay name for the pipeline
slug requiredstringURL-safe slug (e.g. my-pipeline)
workflow_ids requiredint[]Ordered list of tool IDs (from /workflows)
flow_config optionalobjectNode-based pipeline config. Contains nodes (array of node objects with tool slug and per-param config) and output_node (ID of final node). Each param can be expose (user API param), fixed (baked-in), or from_node (wired from previous node).
Legacy pipeline (step-based)
curl -X POST https://apiai.me/api/flows \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Pipeline",
    "slug": "my-pipeline",
    "workflow_ids": [3, 5]
  }'
Node-based pipeline (clean params)
curl -X POST https://apiai.me/api/flows \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Generate & Remove BG",
    "slug": "gen-remove-bg",
    "workflow_ids": [3, 5],
    "flow_config": {
      "nodes": [
        {
          "id": "node_1",
          "workflow": "seedream-4",
          "params": {
            "prompt": {"source": "expose", "expose": "prompt", "required": true},
            "style": {"source": "fixed", "fixed": "photorealistic"}
          }
        },
        {
          "id": "node_2",
          "workflow": "remove-background",
          "params": {
            "image": {"source": "from_node", "from_node": "node_1", "is_image": true}
          }
        }
      ],
      "output_node": "node_2"
    }
  }'

Execute Pipeline

POST /flow/{slug}

Run a custom pipeline. Data passes through each step sequentially. For node-based pipelines, use the clean parameter names shown in /flows. For legacy pipelines, parameters are step-suffixed (e.g. prompt_1).

Pipelines may include gate nodes that validate content before proceeding. If a gate rejects the input, the pipeline stops early and returns 422 Unprocessable Entity with an error message explaining why. Gates can expose a named input parameter (e.g. check_image) so the API caller can provide content specifically for the gate to evaluate.

If an exposed image input supports multiple uploads (for example a first step backed by a workflow with max_images > 1), repeat that same multipart field name multiple times in one request.

FieldTypeDescription
image optionalfileInput image. Required only if the pipeline's first step requires one (check first_step_requires_image in GET /flows). For node-based pipelines, use the exposed param name. Repeat the field to send multiple images when the pipeline supports it.
prompt optionalstringText prompt (for pipelines whose first step supports generation)
{param} optionalstringAny pipeline parameter. Node-based pipelines use clean names (e.g. style, aspect_ratio); legacy pipelines use step-suffixed names (e.g. style_1).
Node-based pipeline (clean params)
curl -X POST https://apiai.me/api/flow/gen-remove-bg \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=a cute dog on white background" \
  --output result.png
Image through pipeline
curl -X POST https://apiai.me/api/flow/my-pipeline \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@input.png" \
  --output result.png
Multi-image pipeline input
curl -X POST https://apiai.me/api/flow/my-pipeline \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@input-1.png" \
  -F "image=@input-2.png" \
  --output result.png
Prompt-only (generate + process)
curl -X POST https://apiai.me/api/flow/create-and-remove-bg \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=a cute dog on a white background" \
  --output result.png
POST /flow/{slug}/debug

Run a single step of a pipeline in isolation for debugging. Same multipart body as /flow/{slug}, plus a step_index (0-based) or node_id field selecting which step to execute. Returns the step's output along with timing and any error detail. Useful for verifying gate decisions, parameter wiring, or partial outputs without re-running the whole pipeline.

Example
curl -X POST https://apiai.me/api/flow/my-pipeline/debug \
  -H "X-API-Key: ak_xxxx" \
  -F "step_index=1" \
  -F "image=@input.png"

Quality Gates

Quality gates are special pipeline nodes that evaluate content (image, text, or both) against a custom prompt and return a YES or NO decision. They are powered by a fast AI model (~$0.001 per evaluation) and enable automated content moderation, quality control, and conditional routing inside pipelines.

How it works

A gate node receives the output from the previous step (image and/or text) and evaluates it against the gate prompt — a plain-language question like "Is this image safe for all audiences?" or "Does the product appear clearly?". The AI responds YES or NO.

Branching

Each branch (YES / NO) can be configured to:

ActionBehaviour
nextContinue to the next node in the pipeline (default for YES)
stopStop the pipeline and return a message (default for NO)
{node_id}Jump to a specific node — enables conditional routing

Response behaviour

When a gate stops the pipeline:

BranchHTTP StatusResponse body
NO → stop422{"error": "...", "gate_node": "...", "gate_response": "...", "cost": ...}
YES → stop200{"message": "...", "gate_node": "...", "gate_response": "...", "cost": ...}

When a gate continues (next or branch), the previous image and text are forwarded to the target node automatically.

Custom messages

Each branch can define a custom message returned when the pipeline stops at that branch. If no message is set, defaults are used ("Content did not pass quality gate" for NO, "Pipeline completed" for YES).

Use cases

  • Content moderation — block unsafe, off-brand, or policy-violating outputs before delivery
  • Quality control — reject blurry, cropped, or low-quality generated images
  • Conditional routing — send YES results to one processing path and NO results to another
  • Approval workflows — gate on subjective criteria like brand consistency or style match

Quality Gate

POST /v1/quality-gate

Ask any YES/NO question about an image and/or text. A general-purpose evaluation endpoint powered by fast AI (~$0.001 per call). Use for content moderation, quality checks, object detection, classification, and more. This is the standalone version of the Quality Gate node used inside pipelines.

Request

Send as multipart/form-data:

ParameterTypeRequiredDescription
promptstringYesA YES/NO question to evaluate (e.g. "Does this image contain a person?")
imagefileNo*Image file to evaluate (JPEG, PNG, WebP, GIF)
image_base64stringNo*Alternative: base64-encoded image data
textstringNo*Text content to evaluate
urlstringNo*http(s) URL to fetch and evaluate. The server downloads the URL (max 5 MB, 10 s timeout, SSRF-protected) and converts it to evaluable input: HTML pages → readable text (article body extraction), plain text / Markdown / JSON / XML / RSS / Atom → text, images (png/jpeg/webp/gif) → image bytes. PDFs, video, audio, and unknown binary types are rejected.

* At least one of image, image_base64, text, or url is required alongside the prompt.

Response (200 OK)

Success response
{
  "version": "1.0",
  "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "answer": true,
  "response": "YES",
  "prompt": "Does this image contain a person?",
  "latency_ms": 312
}

Response fields

FieldTypeDescription
answerbooleantrue = YES, false = NO
responsestringRaw model response text
promptstringThe prompt that was evaluated
latency_msintegerEvaluation time in milliseconds

Example prompts

Use casePrompt
Person detectionDoes this image contain a person?
Quality checkIs this a high-quality, well-lit photograph?
TransparencyDoes this image have a transparent background?
NSFW filterDoes this image contain NSFW or inappropriate content?
Text detectionDoes this image contain readable text?
Brand checkDoes this image contain a company logo?
Text moderationIs this text toxic or offensive? (with text field)

curl examples

Check an image
curl -X POST https://apiai.me/api/v1/quality-gate \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=Does this image contain a person?" \
  -F "image=@photo.jpg"
Check text content
curl -X POST https://apiai.me/api/v1/quality-gate \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=Is this text toxic or offensive?" \
  -F "text=Hello, this is a friendly message"
Check both image and text
curl -X POST https://apiai.me/api/v1/quality-gate \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=Does this product image match the description?" \
  -F "image=@product.jpg" \
  -F "text=Red leather handbag with gold clasp"
Evaluate a remote URL (blog post, landing page, image)
curl -X POST https://apiai.me/api/v1/quality-gate \
  -H "X-API-Key: ak_xxxx" \
  -F "prompt=Is this article well-structured and free of factual errors?" \
  -F "url=https://example.com/blog/post-slug"

Moderation: Check Image

POST /v1/moderation/check-image

Evaluate an image against a configurable policy and receive a structured JSON decision. Always returns 200 for successful checks — the decision (allow, reject, or review) is in the response body, not the HTTP status code. Powered by a fast AI model (~$0.001 per check).

Request

Send as multipart/form-data:

ParameterTypeRequiredDescription
imagefileYes*Image file to check (JPEG, PNG, WebP, GIF)
image_base64stringYes*Alternative: base64-encoded image data
policystringNoBuilt-in policy slug (see table below) or any custom label. Default: general
policy_promptstringNoCustom moderation instructions. When supplied, overrides the built-in policy prompt.
metadataJSON stringNoCaller metadata (passed through for your reference)

* Provide either image or image_base64, not both.

Built-in policies

Pass one of these slugs as policy and omit policy_prompt to use the pre-built moderation instructions. You can override any preset by supplying your own policy_prompt.

Policy slugWhat it checks
generalGeneral appropriateness — offensive, violent, or explicit content (default)
nsfwSexually explicit or adult content, nudity
adultAlias for nsfw
violenceGraphic violence, gore, weapons used against people
brand-safetySafe for mainstream brand advertising (combines NSFW + violence + hate)
product-photoE-commerce quality — clear, well-lit product on clean background
hate-speechHate symbols, extremist imagery, dehumanising content
spamJunk, watermarked stock photos, auto-generated filler, test patterns

For domain-specific checks (e.g. "does this image contain our product logo?") use a custom policy label and supply policy_prompt with your own instructions.

Response (200 OK)

Success response
{
  "version": "1.0",
  "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "decision": "reject",
  "confidence": 0.97,
  "policy": "dog-only-v1",
  "reasons": [
    {
      "code": "NO_DOG_DETECTED",
      "message": "No dog detected in image"
    }
  ],
  "signals": {
    "dog_detected": false,
    "nsfw": false
  },
  "latency_ms": 412
}

Decision values

DecisionMeaningWhen to use
allowImage passes the policyProceed with processing
rejectImage fails the policyBlock or discard
reviewAmbiguous / low confidenceQueue for human review

HTTP status codes

StatusMeaning
200Check completed — decision is in the body
400Invalid request (missing image, bad mime type, too large)
401/403Authentication error
402Insufficient balance
429Rate limit exceeded
500Internal moderation failure

Error responses (non-200)

Error shape
{"error": {"code": "INVALID_IMAGE", "message": "Unsupported image type"}}

Response headers

HeaderDescription
X-Request-IDUnique request ID (send your own via header to correlate)
X-Processing-TimeServer-side latency in ms
X-CostCost charged for this check
X-Balance-RemainingYour remaining balance

curl examples

NSFW check (built-in policy)
curl -X POST https://apiai.me/api/v1/moderation/check-image \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo.jpg" \
  -F "policy=nsfw"
Brand-safety check (built-in policy)
curl -X POST https://apiai.me/api/v1/moderation/check-image \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo.jpg" \
  -F "policy=brand-safety"
Custom policy with your own instructions
curl -X POST https://apiai.me/api/v1/moderation/check-image \
  -H "X-API-Key: ak_xxxx" \
  -F "image=@photo.jpg" \
  -F "policy=dog-only-v1" \
  -F 'policy_prompt=Check whether the image contains a dog as the main subject'
Check with base64
curl -X POST https://apiai.me/api/v1/moderation/check-image \
  -H "X-API-Key: ak_xxxx" \
  -F "image_base64=$(base64 -i photo.jpg)" \
  -F "policy=nsfw"
Custom request ID for correlation
curl -X POST https://apiai.me/api/v1/moderation/check-image \
  -H "X-API-Key: ak_xxxx" \
  -H "X-Request-ID: my-correlation-id-123" \
  -F "image=@photo.jpg"

Pipeline integration

This endpoint uses the same evaluation engine as Quality Gates in pipelines. When a gate node is used inside a pipeline, downstream condition nodes can branch on decision, confidence, and reasons[].code from the structured moderation result.

Delete Pipeline

DELETE /flows/{id}

Delete a custom pipeline by its ID.

Example
curl -X DELETE https://apiai.me/api/flows/42 \
  -H "X-API-Key: ak_xxxx"
Response 200
{ "ok": true }

MCP Server

Use apiai.me directly from AI-enabled editors and local AI tools like VS Code (Copilot), Cursor, Windsurf, Ollama, and others that support the Model Context Protocol. No binary to install — just point your client at our hosted MCP endpoint.

MCP https://apiai.me/mcp No API key required

Browse all available tools without an account. Point any MCP client at https://apiai.me/mcp — no headers needed. Your AI assistant will see every tool with full descriptions and parameters. Calling a tool will prompt you to get a free API key.

MCP https://apiai.me/api/mcp

Authenticated endpoint. Uses the Streamable HTTP transport. Authenticate with your API key via the X-API-Key header.

VS Code — .vscode/mcp.json

VS Code
{
  "servers": {
    "apiai": {
      "type": "http",
      "url": "https://apiai.me/api/mcp",
      "headers": {
        "X-API-Key": "ak_xxxx"
      }
    }
  }
}

Cursor — .cursor/mcp.json

Cursor
{
  "mcpServers": {
    "apiai": {
      "url": "https://apiai.me/api/mcp",
      "headers": {
        "X-API-Key": "ak_xxxx"
      }
    }
  }
}

Windsurf — ~/.codeium/windsurf/mcp_config.json

Windsurf
{
  "mcpServers": {
    "apiai": {
      "url": "https://apiai.me/api/mcp",
      "headers": {
        "X-API-Key": "ak_xxxx"
      }
    }
  }
}

Ollama — ~/.ollama/mcp.json

Ollama
{
  "mcpServers": {
    "apiai": {
      "url": "https://apiai.me/api/mcp",
      "headers": {
        "X-API-Key": "ak_xxxx"
      }
    }
  }
}

Available tools

Once connected, your AI assistant has access to these tools:

ToolDescription
list_workflowsDiscover available tools, parameters, and pricing
list_flowsList your custom pipelines
generate_imageGenerate an image from a text prompt
edit_imageProcess an image through a tool (base64 input/output)
run_flowExecute a pipeline
check_balanceCheck your account balance
get_accountGet account details
list_batchesList your batch processing jobs
get_batchGet detailed status of a batch job (per-item results, progress, cost)
cancel_batchCancel a running batch job
quality_gateAsk a YES/NO question about an image, text, or URL
check_imageRun a moderation check on an image (returns allow/reject/review)

Example prompts

Things you can ask your AI assistant
"Generate a minimalist tech logo using apiai"
"Remove the background from this image (pass base64)"
"Run my create-and-remove-bg pipeline with prompt 'a cute dog'"
"What tools do I have access to?"
"Check my apiai balance"

Quality Monitoring (Auto-Eval)

Quality Monitoring lets an AI judge score the output of your pipelines, ad-hoc content, or remote URLs against a profile you define. Each evaluation produces a 0–100 score, optional per-dimension scores, a verdict (pass, review, or fail), written reasoning, and improvement hints. Everything is managed at eval.apiai.me using your existing apiai.me API key.

There are three ways to trigger an eval:

  1. Auto-Eval — every run of an opted-in pipeline is scored automatically after it completes.
  2. Monitors — the system periodically fetches a URL or RSS feed and scores new/changed content on a schedule.
  3. ManualPOST /v1/eval/run with an image, text, or URL whenever you want a one-off score.

Results stream live to the dashboard, and verdicts can trigger email notifications to one or more recipients.

Eval Profiles

A profile is the “rubric” the AI judge uses. It defines what you care about in plain English, optional weighted scoring dimensions, which pipelines it should auto-score, and — for multi-input pipelines — how each input is referenced in the rubric.

SettingTypeDescription
namestringHuman-readable profile name (required).
goalsstringPlain-English description of what good output looks like and why you're measuring it (required).
rubricstringProse scoring rules the judge follows. Reference inputs by their typed tokens, e.g. "preserve the geometry of {image_1} and apply the fabric from {image_2}". Tokens are derived from the watched pipeline — see Input Slots.
good_criteriastring[]Specific things that should be present (e.g. "clean white background", "product fully visible").
bad_criteriastring[]Specific things that should be absent (e.g. "watermarks", "distorted faces", "clickbait phrasing").
dimensionsobject[]Optional weighted scoring axes. Each dimension is { name, description, weight }. The overall score is the weighted average of dimension scores; if no dimensions are defined the judge returns a single overall score.
pass_thresholdnumberMinimum score for a pass verdict. Default 70.
evaluatorstringWhich Gemini model judges the output. Defaults to the configured fast model (currently gemini-2.5-flash-lite).
auto_evalbooleanWhen true, every matching pipeline run is scored automatically.
auto_eval_slugsstring[]Explicit list of pipeline / tool slugs the profile watches (the “Watches” list in the dashboard). Required for auto-eval — an empty list means the profile is opt-in only and never fires automatically. This prevents an unrelated rubric (e.g. a blog-content profile) from accidentally scoring image-generation runs. The first watched pipeline also drives the profile's Input Slots.
input_slotsobject[]Typed input bindings derived from the watched pipeline. Each slot is { source_name, token, role, alias? } where role is original (the output is judged against this) or context (visible to the judge but not a comparison anchor). See Input Slots.
eval_costnumberCost per evaluation deducted from your org balance. Defaults to $0.02.
is_activebooleanDisable the profile without deleting it. Inactive profiles never run.

Input Slots & Tokens

When a profile watches a pipeline, the eval service inspects that pipeline's parameter schema and creates one input slot per parameter. Each slot gets a generic, type-prefixed token you reference in the rubric — {image_1}, {image_2}, {prompt_1}, {image_mask_1}, etc. The pipeline's real parameter name (e.g. product_image) stays internal and is never shown to the judge, so a single rubric stays portable across pipelines with the same shape.

For each slot you choose:

  • Original — the output is judged against this input (e.g. preserve geometry from the source photo, match the fabric from a swatch). Multiple originals are normal.
  • Context — visible to the judge but not a comparison anchor (e.g. masks, control inputs, guidance prompts).
  • Alias (optional) — a friendlier label shown alongside the token in the dashboard. Doesn't change the token itself.

Token ordinals are locked at profile creation time, driven by the watched pipeline's parameter order (required-first). If new parameters are added to the pipeline later they get fresh ordinals — existing tokens never shift, so a published rubric keeps meaning the same thing.

For auto-eval, when a pipeline run completes, each user-supplied input is forwarded to the eval service and bound to the matching slot by parameter name. The judge then receives the rubric (with tokens left in place so it can reference them in its reasoning) plus a typed manifest per slot.

Verdicts

The verdict is derived from the overall score and the profile's pass_threshold:

VerdictRuleMeaning
passscore ≥ pass_thresholdOutput meets quality criteria.
reviewpass_threshold − 10 ≤ score < pass_thresholdBorderline — manual review recommended.
failscore < pass_threshold − 10Output does not meet criteria.

Billing

Each evaluation costs $0.02 by default (configurable per profile) and is deducted from your org balance. The cost is reserved up-front and refunded if the judge call fails. If your balance is insufficient, the eval is skipped — your pipeline run itself is not affected.

Manual Eval — POST /v1/eval/run

Score arbitrary content against a profile on demand. Authenticate with your apiai.me API key (X-API-Key). Submit as multipart/form-data:

FieldTypeDescription
profile_idintegerThe profile to evaluate against (required).
imagefileImage file to score (PNG, JPEG, WebP, GIF). Up to 20 MB.
image_base64stringAlternative to image — raw base64 of the image.
textstringText content to score (article, caption, response, etc.).
urlstringhttp(s) URL to fetch and score. The server downloads it (max 5 MB, SSRF-protected) and converts HTML → readable text, plain text/Markdown/JSON/XML/RSS/Atom → text, or images → image bytes.
input_image, input_image_2input_image_4fileUp to 4 reference / “before” images to give the judge context for before/after comparisons. Each also accepts a _base64 variant.
workflow_slugstringOptional label so results can be filtered/grouped on the dashboard.

At least one of image / image_base64 / text / url must be supplied. The response contains the full EvalResult (score, dimension scores, verdict, reasoning, improvement hints, latency, cost).

Monitors — proactive URL & RSS scoring

A Monitor points at a URL or RSS/Atom feed and periodically fetches and scores its content against a profile. Use them to track competitor pages, blog feeds, social posts, status pages, or any public content you care about — without writing your own scheduler.

SettingDescription
nameHuman-readable monitor name.
profile_idProfile used to score fetched content.
source_typeurl (single page/image) or rss (feed of items).
sourceThe URL to fetch.
run_intervalmanual or daily. Daily monitors are picked up by the scheduler and run automatically.
modeWhat to score on each run:
always — always score the current content.
changed — only score when the content hash changes since the last run.
new_only — (RSS) only score feed items the monitor hasn't seen before.
max_per_runCap on how many feed items to score per run (default 10).
is_activeDisable without deleting.

Monitors store their own state (last_hash, seen_ids, last_run_at, last_status, last_error). For URL monitors, when the fetched HTML page exposes an og:image, the image is also pulled and attached so the judge can score prose and visuals together. Each item costs the same as a manual eval (the profile's eval_cost).

Monitors can also be triggered immediately from the dashboard (POST /v1/eval/monitors/:id/run) for testing.

Email Notifications

Add recipients (name + email) and choose which verdicts trigger an email. Whenever a saved eval result has a matching verdict, the system dispatches an email via MailerSend with the score, profile name, reasoning, and a link back to the result on eval.apiai.me.

FieldDescription
nameRecipient display name.
emailEmail address. Must be unique per user.
triggersSubset of ["fail", "review"]. Empty means the recipient receives nothing. pass verdicts never trigger emails.
is_activeDisable without deleting.

Every dispatch — success or failure — is recorded in the Notifications log on the dashboard so you can see exactly what was sent and to whom. You can also fire a synthetic test email from any recipient row to verify the setup end-to-end.

Dashboard

Manage everything at eval.apiai.me — sign in with your apiai.me API key.

  • Profiles — create and edit rubrics, set thresholds, choose which pipelines auto-eval watches.
  • Results — live-streaming feed of every evaluated run. Each row shows the output, the score / verdict / dimension scores, the judge's reasoning, and an inputs panel where every bound input appears as a typed slot labeled with its token ({image_1}, {image_2}, …) and the pipeline's source field name. Filter by verdict, profile, or pipeline slug.
  • Analytics — pass/review/fail rates and average scores grouped by profile and pipeline.
  • Monitors — configure URL/RSS monitors, see last-run status, run on demand.
  • Recipients & Notifications — manage who gets emailed for which verdicts and review delivery history.

Teams & Organizations

Every account belongs to an organization. By default your org is solo — just you. You can invite colleagues to join your org so they share your tool access, billing balance, and pipelines without needing separate accounts or separate billing.

Roles

RoleWhat they can do
owner Full control — manage members, send invitations, change roles, rename the org. The org balance is owned by this account.
developer Has their own API key. Can call all tools and pipelines the org has access to. Cannot manage team membership.
designer Dashboard access only — can use the playground and view results. No API key access.

How to invite someone

Open the Dashboard and go to the Team page. Enter your colleague's email address, choose their role, and click Send Invite. They'll receive an email with a link to join your organization.

If the email doesn't arrive, you can copy the invite link directly from the Pending Invitations table and share it manually.

Shared billing

All API calls made by any member — regardless of role — are charged against the owner's balance. Members do not need to add funds separately. The owner can top up the balance from the Funds page and optionally enable auto-refill so the team never runs dry.

Managing your team

Everything is handled from the Team page in the Dashboard:

  • Remove a member — click the remove button next to their name. Their API key is immediately invalidated.
  • Change a role — use the role selector in the members table.
  • Cancel a pending invite — click cancel next to the invitation before it is accepted.
  • Rename your org — use the Organization name field at the bottom of the Team page. The name appears in invite emails.
  • Leave a team — non-owner members can leave from the Team page. After leaving you are moved to a personal solo organization.

Team API

Every Team page action is also available through the API. All endpoints require X-API-Key and operate on the org of the calling key. Owner role is required for membership changes (invite, remove, role change, rename, cancel).

MethodPathDescription
GET/teamReturns the org (id, name) and the list of members and pending invitations.
POST/team/inviteInvite a member. JSON body: { "email": "...", "role": "developer" | "designer" }. Returns the invitation with a shareable token.
DELETE/team/members/:userIdRemove a member from the org. Their API key is invalidated.
PUT/team/members/:userId/roleChange a member's role. JSON body: { "role": "developer" | "designer" }.
DELETE/team/invitations/:invIdCancel a pending invitation.
POST/team/invite/:token/acceptAccept an invitation (the calling key joins the inviting org). Public read of GET /team/invite/:token is also available without auth to preview the invite.
DELETE/team/leaveLeave the current org. The caller is moved into a personal solo org. Owners cannot leave — transfer ownership or delete the org first.
PUT/team/org/renameRename the org. JSON body: { "name": "..." }. Owner only.
Invite a developer
curl -X POST https://apiai.me/api/team/invite \
  -H "X-API-Key: ak_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"email":"colleague@example.com","role":"developer"}'