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": "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.
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
Request Body
| Field | Type | Description |
|---|---|---|
| email required | string | The email associated with your account |
curl -X POST https://apiai.me/api/login \
-H "Content-Type: application/json" \
-d '{"email": "you@company.com"}'
{ "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
Request Body
| Field | Type | Description |
|---|---|---|
| email required | string | Same email used in the login request |
| code required | string | The 6-digit code from the email |
curl -X POST https://apiai.me/api/verify \
-H "Content-Type: application/json" \
-d '{"email": "you@company.com", "code": "482916"}'
{ "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.
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:
| Limit | Scope | Value |
|---|---|---|
| Concurrent execution requests | Per user | 2 in-flight |
| Concurrent execution requests | Server-wide | 2 in-flight |
| Concurrent lightweight requests (reads, dashboard) | Server-wide | 10 in-flight |
| Request body size | Per request | 50 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:
{ "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:
{ "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
List all tools available to your account. Each tool has a slug used in the process endpoint.
curl https://apiai.me/api/workflows \ -H "X-API-Key: ak_xxxx"
[
{
"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
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-bodyso curl exits non-zero on errors instead of writing the JSON error intoresult.png. - For prompts with quotes, commas, or other special characters, single-quote the value:
-F 'prompt=she said "go"'. ak_xxxxis a placeholder — paste your real key (starts withak_) from the Dashboard.
Parameters
| Field | Type | Description |
|---|---|---|
| image optional | file / 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 optional | string | Text prompt — required for generation tools, optional for editing tools. |
| output_filename optional | string | Override 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} optional | string | Any 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_type | Content-Type | Save with |
|---|---|---|
image (default) | image/png, image/jpeg, etc. | --output result.png |
video | video/mp4 | --output result.mp4 |
text | application/json | --output result.json |
curl -X POST https://apiai.me/api/process/your-tool-slug \ -H "X-API-Key: ak_xxxx" \ -F "image=@photo.png" \ --output result.png
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
# 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
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 type | Meaning |
|---|---|
created | Prediction submitted to Replicate |
starting | Replicate is booting the model |
processing | Model is actively generating (sent on each poll) |
done | Job complete — contains result_id and content_type |
error | Job failed — contains message |
# 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:
| Header | Description |
|---|---|
| X-Request-ID | Unique request identifier (UUID) |
| X-Processing-Time | Server-side processing time in ms |
| X-Cost | Cost charged for this request in USD |
| X-Balance-Remaining | Your account balance after this request |
| X-Workflow | The workflow or pipeline slug that was executed |
| X-Pipeline-Steps | Number of steps (only for multi-step pipelines) |
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.
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.
Execute a multi-step pipeline. Pipelines chain multiple tools together — the output of each step feeds into the next.
Parameters
| Field | Type | Description |
|---|---|---|
| image required | file | The input image |
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.
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
| Field | Type | Description |
|---|---|---|
| workflow required | string | Workflow 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 required | file[] | Multiple image files (max limit set by server, default 50) |
| any param | string | Additional workflow/pipeline-specific parameters (applied to all images) |
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"
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.
List your batch jobs (most recent first, max 50).
curl https://apiai.me/api/batch \ -H "X-API-Key: ak_xxxx"
Get batch job status and all item details.
curl https://apiai.me/api/batch/42 \ -H "X-API-Key: ak_xxxx"
Cancel a running batch job. Items already completed are kept; remaining items are skipped.
curl -X POST https://apiai.me/api/batch/42/cancel \ -H "X-API-Key: ak_xxxx"
Download all completed results as a ZIP archive. Available for 1 hour after job completion; multiple downloads are allowed within that window.
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 aggregated usage statistics for your account.
curl https://apiai.me/api/usage \ -H "X-API-Key: ak_xxxx"
Logs
Retrieve recent request logs for your account.
Query Parameters
| Param | Type | Description |
|---|---|---|
| limit optional | int | Number of logs to return (default 50, max 200) |
curl https://apiai.me/api/logs?limit=20 \ -H "X-API-Key: ak_xxxx"
SSE 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().
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)));
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.
Returns your current account balance.
curl https://apiai.me/api/account/balance \ -H "X-API-Key: ak_xxxx"
{ "balance": 47.50 }
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)
| Param | Type | Description |
|---|---|---|
| amount required | number | Top-up amount: 10, 50, or 100 |
curl -X POST https://apiai.me/api/account/create-checkout \
-H "X-API-Key: ak_xxxx" \
-H "Content-Type: application/json" \
-d '{"amount": 50}'
{ "url": "https://checkout.stripe.com/c/pay/..." }
Get your auto-refill settings. When enabled, your saved card is charged automatically when your balance drops below $5.
{ "enabled": true, "amount": 50 }
Enable or disable auto-refill. Requires a saved payment method (make at least one Stripe checkout payment first).
Body Parameters (JSON)
| Param | Type | Description |
|---|---|---|
| enabled required | bool | Turn auto-refill on or off |
| amount required | number | Amount to charge: 10, 50, or 100 |
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}'
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.
curl -X POST https://apiai.me/api/account/regenerate-key \ -H "X-API-Key: ak_xxxx"
{ "api_key": "ak_yyyy..." }
List Pipelines
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).
curl https://apiai.me/api/flows \ -H "X-API-Key: ak_xxxx"
Create Pipeline
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
| Field | Type | Description |
|---|---|---|
| name required | string | Display name for the pipeline |
| slug required | string | URL-safe slug (e.g. my-pipeline) |
| workflow_ids required | int[] | Ordered list of tool IDs (from /workflows) |
| flow_config optional | object | Node-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). |
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]
}'
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
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.
| Field | Type | Description |
|---|---|---|
| image optional | file | Input 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 optional | string | Text prompt (for pipelines whose first step supports generation) |
| {param} optional | string | Any pipeline parameter. Node-based pipelines use clean names (e.g. style, aspect_ratio); legacy pipelines use step-suffixed names (e.g. style_1). |
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
curl -X POST https://apiai.me/api/flow/my-pipeline \ -H "X-API-Key: ak_xxxx" \ -F "image=@input.png" \ --output result.png
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
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
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.
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:
| Action | Behaviour |
|---|---|
next | Continue to the next node in the pipeline (default for YES) |
stop | Stop 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:
| Branch | HTTP Status | Response body |
|---|---|---|
| NO → stop | 422 | {"error": "...", "gate_node": "...", "gate_response": "...", "cost": ...} |
| YES → stop | 200 | {"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
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:
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | A YES/NO question to evaluate (e.g. "Does this image contain a person?") |
image | file | No* | Image file to evaluate (JPEG, PNG, WebP, GIF) |
image_base64 | string | No* | Alternative: base64-encoded image data |
text | string | No* | Text content to evaluate |
url | string | No* | 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)
{
"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
| Field | Type | Description |
|---|---|---|
answer | boolean | true = YES, false = NO |
response | string | Raw model response text |
prompt | string | The prompt that was evaluated |
latency_ms | integer | Evaluation time in milliseconds |
Example prompts
| Use case | Prompt |
|---|---|
| Person detection | Does this image contain a person? |
| Quality check | Is this a high-quality, well-lit photograph? |
| Transparency | Does this image have a transparent background? |
| NSFW filter | Does this image contain NSFW or inappropriate content? |
| Text detection | Does this image contain readable text? |
| Brand check | Does this image contain a company logo? |
| Text moderation | Is this text toxic or offensive? (with text field) |
curl examples
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"
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"
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"
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
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:
| Parameter | Type | Required | Description |
|---|---|---|---|
image | file | Yes* | Image file to check (JPEG, PNG, WebP, GIF) |
image_base64 | string | Yes* | Alternative: base64-encoded image data |
policy | string | No | Built-in policy slug (see table below) or any custom label. Default: general |
policy_prompt | string | No | Custom moderation instructions. When supplied, overrides the built-in policy prompt. |
metadata | JSON string | No | Caller 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 slug | What it checks |
|---|---|
general | General appropriateness — offensive, violent, or explicit content (default) |
nsfw | Sexually explicit or adult content, nudity |
adult | Alias for nsfw |
violence | Graphic violence, gore, weapons used against people |
brand-safety | Safe for mainstream brand advertising (combines NSFW + violence + hate) |
product-photo | E-commerce quality — clear, well-lit product on clean background |
hate-speech | Hate symbols, extremist imagery, dehumanising content |
spam | Junk, 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)
{
"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
| Decision | Meaning | When to use |
|---|---|---|
allow | Image passes the policy | Proceed with processing |
reject | Image fails the policy | Block or discard |
review | Ambiguous / low confidence | Queue for human review |
HTTP status codes
| Status | Meaning |
|---|---|
200 | Check completed — decision is in the body |
400 | Invalid request (missing image, bad mime type, too large) |
401/403 | Authentication error |
402 | Insufficient balance |
429 | Rate limit exceeded |
500 | Internal moderation failure |
Error responses (non-200)
{"error": {"code": "INVALID_IMAGE", "message": "Unsupported image type"}}
Response headers
| Header | Description |
|---|---|
X-Request-ID | Unique request ID (send your own via header to correlate) |
X-Processing-Time | Server-side latency in ms |
X-Cost | Cost charged for this check |
X-Balance-Remaining | Your remaining balance |
curl examples
curl -X POST https://apiai.me/api/v1/moderation/check-image \ -H "X-API-Key: ak_xxxx" \ -F "image=@photo.jpg" \ -F "policy=nsfw"
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"
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'
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"
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 a custom pipeline by its ID.
curl -X DELETE https://apiai.me/api/flows/42 \ -H "X-API-Key: ak_xxxx"
{ "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.
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.
Authenticated endpoint. Uses the Streamable HTTP transport. Authenticate with your API key via the X-API-Key header.
VS Code — .vscode/mcp.json
{
"servers": {
"apiai": {
"type": "http",
"url": "https://apiai.me/api/mcp",
"headers": {
"X-API-Key": "ak_xxxx"
}
}
}
}
Cursor — .cursor/mcp.json
{
"mcpServers": {
"apiai": {
"url": "https://apiai.me/api/mcp",
"headers": {
"X-API-Key": "ak_xxxx"
}
}
}
}
Windsurf — ~/.codeium/windsurf/mcp_config.json
{
"mcpServers": {
"apiai": {
"url": "https://apiai.me/api/mcp",
"headers": {
"X-API-Key": "ak_xxxx"
}
}
}
}
Ollama — ~/.ollama/mcp.json
{
"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:
| Tool | Description |
|---|---|
list_workflows | Discover available tools, parameters, and pricing |
list_flows | List your custom pipelines |
generate_image | Generate an image from a text prompt |
edit_image | Process an image through a tool (base64 input/output) |
run_flow | Execute a pipeline |
check_balance | Check your account balance |
get_account | Get account details |
list_batches | List your batch processing jobs |
get_batch | Get detailed status of a batch job (per-item results, progress, cost) |
cancel_batch | Cancel a running batch job |
quality_gate | Ask a YES/NO question about an image, text, or URL |
check_image | Run a moderation check on an image (returns allow/reject/review) |
Example prompts
"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:
- Auto-Eval — every run of an opted-in pipeline is scored automatically after it completes.
- Monitors — the system periodically fetches a URL or RSS feed and scores new/changed content on a schedule.
- Manual —
POST /v1/eval/runwith 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.
| Setting | Type | Description |
|---|---|---|
name | string | Human-readable profile name (required). |
goals | string | Plain-English description of what good output looks like and why you're measuring it (required). |
rubric | string | Prose 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_criteria | string[] | Specific things that should be present (e.g. "clean white background", "product fully visible"). |
bad_criteria | string[] | Specific things that should be absent (e.g. "watermarks", "distorted faces", "clickbait phrasing"). |
dimensions | object[] | 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_threshold | number | Minimum score for a pass verdict. Default 70. |
evaluator | string | Which Gemini model judges the output. Defaults to the configured fast model (currently gemini-2.5-flash-lite). |
auto_eval | boolean | When true, every matching pipeline run is scored automatically. |
auto_eval_slugs | string[] | 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_slots | object[] | 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_cost | number | Cost per evaluation deducted from your org balance. Defaults to $0.02. |
is_active | boolean | Disable 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:
| Verdict | Rule | Meaning |
|---|---|---|
pass | score ≥ pass_threshold | Output meets quality criteria. |
review | pass_threshold − 10 ≤ score < pass_threshold | Borderline — manual review recommended. |
fail | score < pass_threshold − 10 | Output 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:
| Field | Type | Description |
|---|---|---|
profile_id | integer | The profile to evaluate against (required). |
image | file | Image file to score (PNG, JPEG, WebP, GIF). Up to 20 MB. |
image_base64 | string | Alternative to image — raw base64 of the image. |
text | string | Text content to score (article, caption, response, etc.). |
url | string | http(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_2…input_image_4 | file | Up to 4 reference / “before” images to give the judge context for before/after comparisons. Each also accepts a _base64 variant. |
workflow_slug | string | Optional 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.
| Setting | Description |
|---|---|
name | Human-readable monitor name. |
profile_id | Profile used to score fetched content. |
source_type | url (single page/image) or rss (feed of items). |
source | The URL to fetch. |
run_interval | manual or daily. Daily monitors are picked up by the scheduler and run automatically. |
mode | What 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_run | Cap on how many feed items to score per run (default 10). |
is_active | Disable 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.
| Field | Description |
|---|---|
name | Recipient display name. |
email | Email address. Must be unique per user. |
triggers | Subset of ["fail", "review"]. Empty means the recipient receives nothing. pass verdicts never trigger emails. |
is_active | Disable 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
| Role | What 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).
| Method | Path | Description |
|---|---|---|
GET | /team | Returns the org (id, name) and the list of members and pending invitations. |
POST | /team/invite | Invite a member. JSON body: { "email": "...", "role": "developer" | "designer" }. Returns the invitation with a shareable token. |
DELETE | /team/members/:userId | Remove a member from the org. Their API key is invalidated. |
PUT | /team/members/:userId/role | Change a member's role. JSON body: { "role": "developer" | "designer" }. |
DELETE | /team/invitations/:invId | Cancel a pending invitation. |
POST | /team/invite/:token/accept | Accept 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/leave | Leave 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/rename | Rename the org. JSON body: { "name": "..." }. Owner only. |
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"}'