thirds.ai Keys API
The Keys API from thirds.ai — 2 operation(s) for keys.
The Keys API from thirds.ai — 2 operation(s) for keys.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
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.curl "https://apis.io/api/v1/apis/thirds-ai:thirds-ai-keys-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
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.2.0
info:
title: thirds.ai Keys 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
tags:
- name: Keys
paths:
/v1/keys:
post:
summary: Create an API key
description: Create one API key for the authenticated account. The full secret is returned in this response and never again; only its verifier is kept.
operationId: createApiKey
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewApiKey'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'201':
description: The key was created. This is the only response that ever carries the secret.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyWithSecret'
'400':
description: The name was empty, longer than 100 characters, or held a character that is not printable ASCII.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request body is larger than 16 KiB.
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'
'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'
'409':
description: The account already holds ten active keys. Revoke one before creating another.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
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'
tags:
- Keys
get:
summary: List API keys
description: List one bounded page of the authenticated account's keys, most recently created first. A revoked key stays in this history; its secret never appears here.
operationId: listApiKeys
security:
- bearerAuth: []
parameters:
- name: limit
in: query
required: false
description: The page size. Defaults to 20 and must be from 1 through 100.
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: cursor
in: query
required: false
description: An opaque value from an earlier page's next_cursor.
schema:
type: string
maxLength: 128
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: One page of the account's key history.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyList'
'400':
description: The page size or cursor is not valid.
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'
tags:
- Keys
/v1/keys/{key_id}:
delete:
summary: Revoke an API key
description: Revoke one key of the authenticated account. Revoking a key that is already revoked returns the same answer again rather than an error, so a retried request is never rejected.
operationId: revokeApiKey
security:
- bearerAuth: []
parameters:
- name: key_id
in: path
required: true
description: The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^key_[0-9a-f]{32}$
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The key, now revoked.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKey'
'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'
'404':
description: No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
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'
tags:
- Keys
patch:
summary: Set or clear an API key's monthly overage cap
description: 'Change how many overage credits this key may take on per calendar month (UTC). A positive number sets the cap; an explicit null, or a body without the field, clears it. The cap bounds only overage: work the prepaid balance fully covers is never refused by it.'
operationId: updateApiKey
security:
- bearerAuth: []
parameters:
- name: key_id
in: path
required: true
description: The key's public identifier, such as "key_1f8b3c7d5e2a49061f8b3c7d5e2a4906".
schema:
type: string
pattern: ^key_[0-9a-f]{32}$
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKeyUpdate'
responses:
'405':
$ref: '#/components/responses/MethodNotAllowed'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'
'200':
description: The key with its new cap.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKey'
'400':
description: The cap was zero, negative, not a whole number, or the body held a field this operation does not know.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'413':
description: The request body is larger than 16 KiB.
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'
'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'
'404':
description: No key with this identifier belongs to the authenticated account. This is also the answer for an identifier that does not exist at all, so a request can never learn which one is true.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
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'
tags:
- Keys
components:
schemas:
ApiKey:
type: object
description: One key, without its secret.
properties:
id:
type: string
pattern: ^key_[0-9a-f]{32}$
description: 'The key''s public identifier: "key_" followed by 32 lowercase hexadecimal characters.'
name:
type: string
display_prefix:
type: string
description: The first characters of the secret, enough to tell keys apart in a list.
status:
type: string
enum:
- active
- revoked
created_at:
type: string
format: date-time
revoked_at:
type:
- string
- 'null'
format: date-time
last_used_at:
type:
- string
- 'null'
format: date-time
monthly_overage_cap:
type:
- integer
- 'null'
minimum: 1
description: The most overage credits this key may take on per calendar month (UTC). Null means the key sets no cap of its own.
required:
- id
- name
- display_prefix
- status
- created_at
- revoked_at
- last_used_at
- monthly_overage_cap
additionalProperties: false
ApiKeyList:
type: object
description: One bounded page of the account's key history.
properties:
data:
type: array
maxItems: 100
items:
$ref: '#/components/schemas/ApiKey'
next_cursor:
type:
- string
- 'null'
maxLength: 128
required:
- data
- next_cursor
additionalProperties: false
FieldDetail:
type: object
description: One request field that failed, its fixed safe reason, and an optional bounded source location. Details never carry template source, customer values, rendered output, or raw evaluator prose.
properties:
field:
type: string
maxLength: 260
description: The path to the field, such as "pdf.scale". Schema errors use data followed by a JSON Pointer, such as data/items/0/count. The pointer is cut at 256 UTF-8 bytes. Empty for a problem with the whole document.
reason:
type: string
enum:
- malformed JSON
- missing field
- unknown field
- wrong type
- invalid value
- A required value is missing.
- Use the expected value type.
- Declare this variable before using it.
- Choose an allowed value.
- Use the required format.
- Add a value.
- Use a shorter value.
- Use a number within the allowed range.
- Check this value against its data rule.
description: A fixed, safe reason. It never repeats the value the caller sent.
line:
type: integer
minimum: 1
maximum: 1000000
description: The one-based template source line when the evaluator provides one within the published bound.
column:
type: integer
minimum: 1
maximum: 1000000
description: The one-based template source column when the evaluator provides one within the published bound.
required:
- field
- reason
additionalProperties: false
ApiKeyWithSecret:
type: object
description: One key exactly as creation answers it. This is the only shape that ever carries the secret.
properties:
id:
type: string
pattern: ^key_[0-9a-f]{32}$
description: 'The key''s public identifier: "key_" followed by 32 lowercase hexadecimal characters.'
name:
type: string
secret:
type: string
pattern: ^thirds_sk_v1_[0-9a-f]{64}$
description: 'The full secret: "thirds_sk_v1_" followed by 64 lowercase hexadecimal characters. It is shown here once and never again.'
display_prefix:
type: string
description: The first characters of the secret, enough to tell keys apart in a list.
status:
type: string
enum:
- active
created_at:
type: string
format: date-time
required:
- id
- name
- secret
- display_prefix
- status
- created_at
additionalProperties: false
ErrorEnvelope:
type: object
description: The one error shape every backend response uses.
properties:
error:
type: object
properties:
code:
type: string
enum:
- account_suspended
- account_concurrency_limited
- abuse_limited
- ai_failure_limit_reached
- ai_needs_paid_credits
- already_subscribed
- auth_unavailable
- batch_row_limit
- billing_unavailable
- brand_asset_account_limit_reached
- brand_asset_invalid
- brand_asset_kind_invalid
- brand_asset_kind_mismatch
- brand_asset_limit_reached
- brand_asset_too_large
- brand_asset_type_unsupported
- brand_data_conflict
- brand_font_glyphs_exceeded
- brand_font_tables_invalid
- brand_image_animated
- brand_image_dimensions_invalid
- brand_image_pixels_exceeded
- brand_kit_colours_invalid
- brand_kit_conflict
- brand_kit_limit
- brand_kit_name_invalid
- brand_kit_not_found
- brand_kit_patch_empty
- brand_kit_tone_invalid
- brand_kit_unavailable
- captcha_rejected
- checkout_superseded
- cross_origin_rejected
- csrf_rejected
- download_expired
- email_already_set
- gallery_template_not_found
- generated_template_invalid
- idempotency_conflict
- image_asset_header_unsupported
- image_asset_invalid
- image_asset_limit
- image_asset_not_found
- image_asset_reference_invalid
- image_asset_too_large
- image_asset_type_unsupported
- image_asset_unavailable
- image_url_invalid
- image_url_unavailable
- insufficient_credits
- internal_error
- invalid_cursor
- invalid_email
- invalid_event
- invalid_link
- invalid_profile
- invalid_request
- invalid_upload
- job_not_finished
- key_concurrency_limited
- key_limit_reached
- method_not_allowed
- no_billing_customer
- not_found
- operation_conflict
- operation_limit_exceeded
- operation_pending
- overage_limit_reached
- overage_unavailable
- overloaded
- playground_busy
- playground_request_invalid
- playground_selection_invalid
- playground_session_limited
- playground_unavailable
- policy_version_stale
- provider_unavailable
- rate_limited
- render_probe_busy
- render_probe_failed
- render_probe_not_configured
- render_probe_required
- render_probe_timeout
- request_headers_too_large
- request_too_large
- resize_timeout
- signed_out
- spend_cap_reached
- template_build_not_found
- template_data_collection_limit
- template_data_depth_limit
- template_data_invalid
- template_data_limit
- template_depth_limit
- template_draft_not_found
- template_evaluation_error
- template_invalid_filter_input
- template_missing_data
- template_output_limit
- template_not_found
- template_schema_complexity
- template_schema_draft_unsupported
- template_schema_invalid
- template_schema_too_large
- template_size_canvas_mismatch
- template_size_data_overrides_too_large
- template_size_duplicate_id
- template_size_invalid_dimensions
- template_size_invalid_id
- template_size_invalid_name
- template_sizes_too_large
- template_sizes_too_many
- template_source_limit
- template_syntax_error
- template_timeout
- template_version_changed
- template_work_limit
- testimonial_busy
- testimonial_invalid
- testimonial_rate_limited
- testimonial_unavailable
- unauthorized
- unsupported_media_type
- webhook_limit_reached
description: A fixed, machine-readable error code.
message:
type: string
description: A fixed, human-readable message.
request_id:
type: string
format: uuid
description: The identifier this answer also carries in its x-request-id header.
details:
type: array
description: Present on a validation failure. Schema failures return at most 16 entries with bounded data paths and fixed reasons. Values from the request are never repeated.
items:
$ref: '#/components/schemas/FieldDetail'
retry:
$ref: '#/components/schemas/RetryInfo'
plan_limit:
$ref: '#/components/schemas/PlanLimitInfo'
required:
- code
- message
- request_id
additionalProperties: false
required:
- error
additionalProperties: false
PlanLimitInfo:
type: object
description: 'Present when the account''s plan causes the refusal: brand_kit_limit and batch_row_limit. It names the limit and the plan that sets it, so a caller can act without a second request. The message text never names the number.'
properties:
plan:
type: string
enum:
- scale
- growth
- starter
- pack
- free
description: The account's plan at the time of the refusal.
limit:
type: integer
minimum: 1
description: 'What that plan allows: brand kits for brand_kit_limit, rows in one run for batch_row_limit.'
required:
- plan
- limit
additionalProperties: false
ApiKeyUpdate:
type: object
description: What an update may change about a key.
properties:
name:
type: string
minLength: 1
maxLength: 100
pattern: ^(?=.*[!-~])[ -~]+$
description: New key name. The key secret and prefix stay the same.
monthly_overage_cap:
type:
- integer
- 'null'
minimum: 1
description: The most overage credits this key may take on per calendar month (UTC), or null to clear the cap. Omit it to keep the current cap.
additionalProperties: false
NewApiKey:
type: object
description: What a new key is named.
properties:
name:
type: string
minLength: 1
maxLength: 100
pattern: ^(?=.*[^ ])[ -~]+$
description: 1 to 100 printable ASCII characters.
required:
- name
additionalProperties: false
RetryInfo:
type: object
description: How long the caller must wait before it retries.
properties:
retry_after_seconds:
type: integer
minimum: 1
maximum: 60
required:
- retry_after_seconds
additionalProperties: false
responses:
MethodNotAllowed:
description: The method is not allowed on this route.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
RequestHeadersTooLarge:
description: The request has more than 64 headers or more than 32 KiB of header names and values.
headers:
x-request-id:
$ref: '#/components/headers/XRequestId'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
headers:
XRequestId:
description: The UUID that identifies this request and matches error.request_id on an error response.
required: true
schema:
type: string
format: uuid
securitySchemes:
sessionCookie:
type: apiKey
in: cookie
name: __Host-thirds_session
description: A browser session. Browser writes also require the matching x-csrf-token header from GET /v1/me.
bearerAuth:
type: http
scheme: bearer
description: 'An API key''s secret, sent as "Authorization: Bearer thirds_sk_v1_...".'
x-unmatched-v1-responses:
description: A request below /v1 that matches no operation receives the shared safe envelope. OpenAPI has no standard path item for an unmatched route, so this extension records the fallback contract without claiming that a catch-all operation exists.
'404':
$ref: '#/components/responses/NotFound'
'431':
$ref: '#/components/responses/RequestHeadersTooLarge'