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/hermoso-ai:hermoso-ai-mcp-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
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.2.0
info:
title: Hermoso server and OAuth (not the REST API) MCP API
version: 1.0.0
summary: 'The Hermoso MCP endpoint and its OAuth documents. The REST API at /v1 is described by its own spec, generated from the router: https://app.hermoso.ai/openapi.json'
description: 'Hermoso is an AI marketing studio built to be driven by an agent. The **public programmatic interface is a Model Context Protocol (MCP) server** at `POST https://app.hermoso.ai/mcp`, speaking JSON-RPC 2.0 over the MCP Streamable HTTP transport. One connection exposes the whole loop as tools: research the ads running in a market, generate on-brand image and video ads, publish or schedule them to the brand''s own channels, and build and read paid campaigns.
**This document describes the MCP endpoint and the OAuth documents around it, and nothing else.** It is deliberately small, because the MCP surface genuinely is: the tools are not separate REST paths, they are `tools/call` methods on the single MCP endpoint. Call `tools/list` after `initialize` to enumerate them, or read the generated roster at .
**There is also a public REST API, and it has its own document.** `/v1` covers publishing, scheduling, media, channels and credits on the same `hmk_` bearer token, and an agent with no account can provision one on a paid plan with `POST /v1/signup`. It is not described here, deliberately: its OpenAPI document is GENERATED from the same operations table the router is mounted from, so a second hand-written copy would be a second thing to go stale. Read it at (live, authoritative) or (static copy), and the prose at . `/v1` sends no CORS headers either, on purpose: an API key carries full account authority.
**What genuinely has no contract are the routes under `/api` on the app.** They are shaped for our own web client, they authenticate with a browser session, and they change frequently. They are not documented anywhere because building against them will break. Use `/v1`, MCP or the npm CLI (`npm install -g hermoso`).
## Authenticating
Two methods, both bearer tokens on the `Authorization` header:
- **OAuth 2.1 authorization code + PKCE** — the path an MCP client takes. Dynamic client registration is open (RFC 7591), so a client can register itself with no pre-arranged credentials. Discovery follows RFC 9728 and RFC 8414: the `401` from `/mcp` carries a `WWW-Authenticate: Bearer resource_metadata="…"` header pointing at the protected-resource document.
- **Agent key** — a long-lived `hmk_…` token minted at app.hermoso.ai → MCP & CLI → Terminal & API keys, for scripts and headless agents.
`initialize` answers without a token so a client can discover the server before authenticating. `tools/call` requires one.
## Errors
Every failure has the same JSON shape — `error` (a human-safe string), an optional `connector` key naming a provider that is not linked, and an optional `meta` object of machine-readable hints. The one rule worth memorising: **a 401 without a `connector` field means your key is bad; a 401 with one means that provider is not connected.** Branch on the field''s presence, never on the message text. Full contract: .
## Costs
Generation is metered in credits with exact published per-render prices. Call the `hermoso_capabilities` tool — free and read-only — before rendering: it returns the valid model ids with their exact credit costs and what this workspace has connected. Machine-readable price list: .'
termsOfService: https://hermoso.ai/terms/
contact:
name: Hermoso support
email: admin@hermoso.ai
url: https://hermoso.ai/contact/
license:
name: Proprietary
url: https://hermoso.ai/terms/
servers:
- url: https://app.hermoso.ai
description: Production
security:
- bearerAuth: []
- oauth2:
- hermoso.research
- hermoso.generate
tags:
- name: MCP
description: 'The Model Context Protocol endpoint. This is the API: every Hermoso tool is a `tools/call` method here.'
externalDocs:
description: MCP & CLI guide
url: https://hermoso.ai/docs/mcp/
paths:
/mcp:
summary: MCP Streamable HTTP endpoint
description: The single endpoint every Hermoso tool is called through, using the MCP Streamable HTTP transport.
post:
operationId: mcpSendRequest
tags:
- MCP
summary: Send a JSON-RPC request to the MCP server
description: 'Sends one JSON-RPC 2.0 message to the Hermoso MCP server and returns its reply.
The usual sequence is `initialize` (which answers without a token, so a client can discover the server first), then `tools/list` to enumerate the available tools, then `tools/call` to run one. `resources/list` and `resources/read` expose the workspace''s readable resources.
The reply is `application/json` for a single response, or `text/event-stream` when the server streams progress notifications before the result — set `Accept: application/json, text/event-stream` and handle both. The server returns an `Mcp-Session-Id` header on `initialize`; send it back on every subsequent request in the session.
Long-running work (a video render) is a job: the tool returns a job id immediately and the agent polls `get_job`.'
security:
- bearerAuth: []
- oauth2:
- hermoso.research
- hermoso.generate
- {}
parameters:
- $ref: '#/components/parameters/McpSessionId'
- $ref: '#/components/parameters/McpProtocolVersion'
requestBody:
required: true
description: A single JSON-RPC 2.0 request object.
content:
application/json:
schema:
$ref: '#/components/schemas/JsonRpcRequest'
examples:
initialize:
summary: Handshake (no token required)
value:
jsonrpc: '2.0'
id: 1
method: initialize
params:
protocolVersion: '2025-06-18'
capabilities: {}
clientInfo:
name: my-agent
version: 1.0.0
toolsList:
summary: Enumerate the available tools
value:
jsonrpc: '2.0'
id: 2
method: tools/list
params: {}
toolsCall:
summary: Run a tool (free, read-only example)
value:
jsonrpc: '2.0'
id: 3
method: tools/call
params:
name: hermoso_capabilities
arguments: {}
responses:
'200':
description: The JSON-RPC reply. A JSON-RPC-level failure is reported in the `error` member of a 200 body; transport and auth failures use the HTTP status codes below.
headers:
Mcp-Session-Id:
description: Returned on `initialize`. Echo it back on every subsequent request in this session.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/JsonRpcResponse'
text/event-stream:
schema:
type: string
description: Server-sent events, each `data:` line carrying one JSON-RPC message.
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/PaymentRequired'
'404':
$ref: '#/components/responses/SessionNotFound'
'429':
$ref: '#/components/responses/RateLimited'
'503':
$ref: '#/components/responses/Unavailable'
get:
operationId: mcpOpenEventStream
tags:
- MCP
summary: Open the server-to-client event stream
description: Opens a Server-Sent Events stream so the server can push notifications and responses that are not the direct reply to a request. Requires an authenticated session; send the `Mcp-Session-Id` returned by `initialize`.
parameters:
- $ref: '#/components/parameters/McpSessionId'
- name: Last-Event-ID
in: header
required: false
description: Resume a dropped stream from the last event the client received.
schema:
type: string
responses:
'200':
description: An open SSE stream.
content:
text/event-stream:
schema:
type: string
description: Server-sent events, each `data:` line carrying one JSON-RPC message.
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/SessionNotFound'
delete:
operationId: mcpEndSession
tags:
- MCP
summary: End the MCP session
description: Explicitly terminates the session named by `Mcp-Session-Id` and releases its server-side state. Sessions also expire on their own after a period of inactivity, so this is a courtesy rather than a requirement.
parameters:
- $ref: '#/components/parameters/McpSessionId'
responses:
'200':
description: The session was terminated and its server-side state released.
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/SessionNotFound'
components:
responses:
Unavailable:
description: 'A provider is at capacity, or a read failed and we will not guess. Retry with backoff. A failed read is reported as a failure rather than as an empty result: "nothing is connected" and "I could not tell you what is connected" are different answers.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PaymentRequired:
description: Out of credits, or the balance cannot cover the estimate for this render. Check `meta.userOutOfCredits` and offer the `buy_credits` tool.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Not enough credits for this render.
meta:
userOutOfCredits: true
BadRequest:
description: A malformed request, or the right call on the wrong surface. The message names the parameter.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: Rate limited, by us or by an upstream platform. Back off and retry. **The connection is fine** — re-authorising cannot clear a rate limit, and the message says so.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: 'Either your key is invalid, revoked or absent (a **bare** 401), or a provider this call needs is not connected for this workspace (a 401 **carrying a `connector` field**). The two need completely different fixes: re-authenticate, versus send the user to link that provider. Branch on whether `connector` is present, never on the message text.'
headers:
WWW-Authenticate:
description: On an unauthenticated MCP request, carries `resource_metadata` pointing at the RFC 9728 document that bootstraps the OAuth flow.
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
badKey:
summary: Your key is bad — re-authenticate
value:
error: Sign in to continue.
meta:
status: 401
connectorMissing:
summary: A provider is not linked — send the user to connect it
value:
error: Connect Meta first (Settings ▸ Connectors ▸ Meta).
connector: meta
SessionNotFound:
description: No such MCP session — it expired or was terminated. Re-run `initialize` to start a new one; the server answers 404 (never 400) precisely so a client knows it must re-initialize.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
parameters:
McpSessionId:
name: Mcp-Session-Id
in: header
required: false
description: The session id returned by `initialize`. Required on every request after the handshake.
schema:
type: string
McpProtocolVersion:
name: MCP-Protocol-Version
in: header
required: false
description: The protocol version the client negotiated during `initialize`.
schema:
type: string
examples:
- '2025-06-18'
schemas:
JsonRpcRequest:
type: object
title: JsonRpcRequest
description: A JSON-RPC 2.0 request, as defined by the Model Context Protocol.
required:
- jsonrpc
- method
properties:
jsonrpc:
type: string
const: '2.0'
description: Always the string `2.0`.
id:
description: Correlates the reply with this request. Omit it to send a notification, which gets no reply.
oneOf:
- type: string
- type: integer
method:
type: string
description: The MCP method to invoke.
examples:
- initialize
- tools/list
- tools/call
- resources/list
- resources/read
- ping
params:
type: object
description: Method parameters. For `tools/call` this is `{ name, arguments }`, where `name` is a tool from `tools/list` and `arguments` matches that tool's own input schema.
additionalProperties: true
properties:
name:
type: string
description: 'For `tools/call`: the tool to run.'
examples:
- hermoso_capabilities
- plan_ad
- render_ad
- post_to_meta
arguments:
type: object
description: 'For `tools/call`: arguments matching the tool''s declared input schema.'
additionalProperties: true
JsonRpcError:
type: object
title: JsonRpcError
description: A JSON-RPC-level failure, returned inside a 200 response.
required:
- code
- message
properties:
code:
type: integer
description: The JSON-RPC error code. `-32700` parse error, `-32600` invalid request, `-32601` method not found, `-32602` invalid params, `-32603` internal error.
examples:
- -32601
- -32602
message:
type: string
description: A short description of the failure.
data:
description: Optional additional detail.
JsonRpcResponse:
type: object
title: JsonRpcResponse
description: A JSON-RPC 2.0 response. Exactly one of `result` or `error` is present.
required:
- jsonrpc
properties:
jsonrpc:
type: string
const: '2.0'
description: Always the string `2.0`.
id:
description: The id of the request this answers.
oneOf:
- type: string
- type: integer
- type: 'null'
result:
type: object
description: The method's result on success. Its shape depends on the method.
additionalProperties: true
error:
$ref: '#/components/schemas/JsonRpcError'
Error:
type: object
title: Error
description: The one error shape every Hermoso route returns. Documented in full at https://hermoso.ai/docs/errors/.
required:
- error
properties:
error:
type: string
description: A human-readable message, safe to show a user. Vendor names, credentials and raw upstream response bodies are scrubbed out of it.
connector:
type: string
description: 'Present only on a connector problem: the provider key that is not linked. Its presence is load-bearing — see the 401 description.'
examples:
- meta
- google_ads
- tiktok
- linkedin
meta:
type: object
description: A small allowlisted set of machine-readable hints. Never the raw upstream message.
additionalProperties: true
properties:
userOutOfCredits:
type: boolean
description: The balance cannot cover this call. Offer `buy_credits`.
suggestModel:
type: string
description: A model id that can serve this request when the one asked for cannot.
contentStop:
type: boolean
description: The model stopped on its own content filter. Change the prompt; do not retry it unchanged.
policyBlock:
type: boolean
description: An advertising-policy refusal. Change the creative; do not retry it unchanged.
status:
type: integer
description: The HTTP status, repeated in the body for clients that only read the payload.
examples:
- error: Connect Meta first (Settings ▸ Connectors ▸ Meta).
connector: meta
- error: 'No such endpoint: GET /api/nonexistent'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: 'A Hermoso agent key (`hmk_…`), minted at app.hermoso.ai → MCP & CLI → Terminal & API keys. Sent as `Authorization: Bearer hmk_…`. A key is pinned to one brand workspace, and that pin is re-authorised on the server for every request — it is never read off a header the caller controls.'
oauth2:
type: oauth2
description: OAuth 2.1 authorization code flow with PKCE (S256). Dynamic client registration is open, so no credentials need to be arranged in advance.
flows:
authorizationCode:
authorizationUrl: https://app.hermoso.ai/oauth/authorize
tokenUrl: https://app.hermoso.ai/oauth/token
scopes:
hermoso.research: 'Names the research surface: the Meta, Google and LinkedIn ad libraries, organic social search, competitor teardowns, and reads of the brand, workspace, credit balance and job status.'
hermoso.generate: 'Names the acting surface: generating creative, publishing to connected channels, and building and reading ad campaigns. Renders consume credits, and any call that arms real ad spend requires an explicit per-call confirmation regardless of the token presented.'
externalDocs:
description: Hermoso developer documentation
url: https://hermoso.ai/docs/