Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/thirdsai-rest-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.1.0
info:
title: thirds.ai API
version: 1.0.0
description: Turn one design into content at scale. Create branded images and PDFs for your campaigns and clients. Automate
each new version through our API or your AI tools. Render saved templates with new data, or send HTML directly. Every
error uses one envelope, every response carries an x-request-id header, and every JSON request body rejects fields it
does not expect.
servers:
- url: https://thirds.ai
paths:
/v1/webhooks/{webhook_id}/test:
post:
summary: Send a signed test event
description: Queue one sample render.succeeded event through the normal delivery worker. It spends no credits and creates
no render. A recorded result schedules no retry, and tests cannot be replayed. Recovery can resend an unfinished attempt
with the same event ID. Disabled destinations can be tested. One test per destination can wait at a time; completed
tests are limited to one per minute. The destination signing secrets and egress policy apply.
operationId: testWebhook
security:
- bearerAuth: []
- sessionCookie: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A valid session or API key is required.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: No destination with this ID belongs to this account.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'200':
description: The test attempt finished.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTest'
'202':
description: The test is still queued or in flight. Read the delivery log for its result before sending another.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTest'
/v1/webhooks/{webhook_id}/deliveries:
get:
summary: Read recent webhook attempts
description: Return up to 20 newest attempts for this destination, in reverse time order. Attempts expire after 30 days.
No request or response body is stored.
operationId: listWebhookDeliveries
security:
- bearerAuth: []
- sessionCookie: []
parameters:
- name: webhook_id
in: path
required: true
description: The destination's public identifier, such as "wh_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^wh_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A valid session or API key is required.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
$ref: '#/components/responses/AccountSuspended'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'404':
description: No destination with this ID belongs to this account.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'200':
description: The latest attempts for this destination.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDeliveryList'
/v1/me/identities/email:
post:
summary: Add an email to an account without one
description: Requires a signed-in browser session and its CSRF token. Sends a one-use verification link bound to that
account and exact session. Open the link in the same browser while signed in, then press Complete sign in. POST /v1/auth/email/confirm
proves the address and grants the current free monthly credits once. An account with an email cannot change it through
this route.
operationId: requestAccountEmail
security:
- sessionCookie: []
parameters:
- name: x-csrf-token
in: header
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AccountEmailRequest'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'401':
description: A live browser session is required. Expired, revoked, and suspended sessions are refused.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: The CSRF token is missing or does not match this browser session.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'429':
description: The request limit was reached. Retry after the time in Retry-After.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: The request failed internally.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'202':
description: The request was accepted. This does not confirm mail delivery or whether another account holds the
address.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/AccountEmailAccepted'
'400':
description: The JSON request is invalid or has unknown fields.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: This account already has an email address. The code is email_already_set.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'422':
description: The email address is invalid. The code is invalid_email.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'503':
description: Email delivery is not configured. The code is auth_unavailable.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image-assets/preview:
post:
summary: Preview a URL image
description: Read a public HTTP or HTTPS PNG, JPEG, or WebP through the checked asset transport. Credentials, private
destinations, unsafe DNS answers, and redirects to them are refused. At most five redirects and 15 seconds total.
The same upload decoder checks the 10 MiB, dimension, pixel, and memory limits. Returns verified bytes without storage
or a render charge. Browser sessions require CSRF.
operationId: previewImageUrl
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
maxLength: 4096
required:
- url
additionalProperties: false
responses:
'200':
description: The verified image bytes and dimensions after orientation.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
x-image-width:
schema:
type: integer
x-image-height:
schema:
type: integer
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-assets:
post:
summary: Upload an image
description: Store one private, immutable PNG, JPEG, or WebP image. Send the raw bytes with the matching Content-Type.
Maximum 10 MiB, 7680 by 4320 stored pixels, 32 million pixels, and 64 MiB decoded memory. Animated images are refused.
Images and brand assets share an account limit of 100 files and 100 MiB. Use the returned reference in an HTML body
img src, or in template data for an img src. Render creation checks ownership after template expansion and captures
the exact bytes. References are not public URLs. PDF header and footer templates do not support image references.
No render credit is charged for an upload.
operationId: uploadImage
security:
- bearerAuth: []
requestBody:
required: true
content:
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
responses:
'201':
description: The image is stored.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/UploadedImage'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/image-assets/{id}/content:
parameters:
- name: id
in: path
required: true
schema:
type: string
pattern: ^asset_[a-f0-9]{32}$
get:
summary: Read an owned image
description: Return verified image or captured brand logo/font bytes to their owner. Missing and foreign assets return
the same 404 error. The response uses private, no-store caching and nosniff.
operationId: readImageContent
security:
- bearerAuth: []
responses:
'200':
description: The exact image bytes.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
font/woff2:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
image/jpeg:
schema:
type: string
format: binary
image/webp:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'404':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'422':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/openapi.json:
get:
summary: Get the OpenAPI document
description: Return this API contract as a JSON document.
operationId: getOpenApiDocument
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The OpenAPI document.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
type: object
/v1/public-stats:
get:
summary: Read measured public facts
description: Return the count of successful customer and playground files, the reviewed gallery template count, and
the weakest uptime and current status from the public status page. The response is cached for five minutes. Customer
content and account data never enter this response.
operationId: getPublicStats
responses:
'200':
description: The measured public facts. Uptime is unavailable when the status provider does not answer.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
cache-control:
schema:
type: string
description: The five-minute public cache policy.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicStats'
'500':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/testimonials:
post:
summary: Offer a testimonial for private review
description: Store one visitor's name, company, quote, and permission for owner review. Sending this request never publishes
the quote. A same-site browser origin and a fresh CAPTCHA pass protect the form.
operationId: submitTestimonial
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TestimonialRequest'
responses:
'201':
description: The testimonial is stored for private owner review.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/TestimonialSubmission'
'400':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
'415':
$ref: '#/components/responses/Error'
'429':
$ref: '#/components/responses/Error'
'500':
$ref: '#/components/responses/Error'
'503':
$ref: '#/components/responses/Error'
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
/v1/pdf:
post:
summary: Create a PDF
description: Render one PDF from raw HTML, an inline stateless template, or one owned saved-template version. An omitted
saved version resolves to one exact immutable version before data validation, admission, idempotency, queueing, or
billing. Template evaluation happens before storage and only evaluated HTML enters render input. Returns 200 for a
terminal job or 202 while the accepted job remains queued or running. A terminal job can be succeeded, failed, or
cancelled; check status before downloading. Send wait=false to skip the bounded wait. Every mode uses the same queue,
renderer, retention, webhook, and fixed one-credit success price.
operationId: createPdf
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: false
description: 1 to 255 printable ASCII characters. A repeated key with the same logical request replays one job; a
different logical request conflicts. Stateless fingerprints include original source, sorted data, and effective
render options. Saved-template fingerprints include the exact resolved template version, sorted data, and effective
options. wait, request IDs, and transport choices do not affect the fingerprint.
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PdfRequest'
examples:
raw_html:
summary: Raw HTML
value:
html: <h1>Quarterly report</h1><p>Ready to share.</p>
stateless_template:
summary: Stateless template with one data object
value:
html: '<h1>{{ report.title }}</h1><p>Total: {{ report.total | currency }}</p>'
data:
report:
title: Quarterly report
total: 125000
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'403':
$ref: '#/components/responses/AccountSuspended'
'404':
$ref: '#/components/responses/Error'
'200':
description: The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render
succeeded.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Idempotency-Replayed:
description: '"true" when this answer replays a job an earlier request with the same idempotency key already
created.'
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'202':
description: The job was accepted and is still queued or running. Poll GET /v1/pdf/{id} for the terminal result.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Idempotency-Replayed:
description: '"true" when this answer replays a job an earlier request with the same idempotency key already
created.'
schema:
type: string
Location:
description: The job's own status URL, /v1/pdf/{id} — where to poll for the terminal result.
schema:
type: string
Retry-After:
description: How soon polling the status URL is worthwhile, in seconds.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/PdfJob'
'400':
description: The request failed validation, saved-template data did not match its schema, bounded template evaluation
failed, or the Idempotency-Key header was the wrong shape.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'402':
description: The account does not hold the one credit required for this render. The code is insufficient_credits.
The reservation settles at one credit on success and is fully released on failure.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'409':
description: This idempotency key was already used for a different logical request, or the selected saved-template
version changed before admission completed.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request is larger than this build accepts.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'415':
description: The request did not carry a JSON content type.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'429':
description: A direct fleet-protection limit was hit. The code is rate_limited, account_concurrency_limited, or
key_concurrency_limited. Each key sustains five requests per second with an idle burst of twenty-one.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
maximum: 60
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'503':
description: The service is at capacity.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
maximum: 60
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
get:
summary: List render history
description: 'List the authenticated account''s render jobs, newest first. Failed or cancelled jobs removed through
DELETE do not appear. Bounded by keyset: pass the previous page''s next_cursor to continue, rather than an offset,
so a page already read stays stable while new jobs are created.'
operationId: listPdfHistory
security:
- bearerAuth: []
parameters:
- name: q
in: query
description: Case-insensitive search of references, template names, and file types.
schema:
type: string
maxLength: 200
pattern: ^[^\u0000-\u001f\u007f-\u009f]*$
- name: limit
in: query
required: false
description: 1 to 100. Defaults to 20. A value outside this range is clamped rather than refused.
schema:
type: integer
default: 20
- name: cursor
in: query
required: false
description: An opaque value from an earlier page's next_cursor. A cursor that cannot be read is refused with 400.
schema:
type: string
maxLength: 128
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: One page of the account's render history.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/PdfHistoryPage'
'400':
description: The limit parameter was not a whole number, the cursor could not be read, or the query held an unknown
parameter.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: The request did not carry a valid, active API key.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
WWW-Authenticate:
description: Always "Bearer" on this response.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'500':
description: An internal error occurred.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
/v1/image:
post:
summary: Create a static image
description: Render one PNG, JPEG, or WebP from raw HTML, an inline stateless template, or one owned saved-template
version. Returns 200 for a terminal job or 202 while the accepted job remains queued or running. A terminal job can
be succeeded, failed, or cancelled; check status before downloading. Send wait=false to skip the bounded wait. A successful
render costs exactly one credit; a failed or cancelled render costs zero.
operationId: createImage
security:
- bearerAuth: []
parameters:
- name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 1
maxLength: 255
pattern: ^[ -~]+$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ImageRequest'
examples:
png:
value:
html: <h1>Quarterly report</h1>
image:
format: png
width: 1280
height: 720
transparent: false
responses:
'200':
description: The job is terminal. Check status for succeeded, failed, or cancelled; HTTP 200 does not mean the render
succeeded.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'202':
description: The image job is durable and remains queued or running.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
Location:
schema:
type: string
Retry-After:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ImageJob'
'400':
$ref: '#/components/responses/Error'
'401':
$ref: '#/components/responses/Error'
'402':
$ref: '#/components/responses/Error'
'403':
$ref: '#/components/responses/AccountSuspended'
'409':
$ref: '#/components/responses/Error'
'413':
$ref: '#/components/responses/Error'
# --- truncated at 32 KB (218 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/thirds-ai/refs/heads/main/openapi/thirds-ai-openapi.yml