First API call in 5 minutes
Everything on this page hits the live production API: https://api.joinpluto.com/v1.
Authentication is a bearer API key (sk_live_..., or sk_test_... while developing).
1. Get a key
Free tier (no card): sign up (opens in a new tab) — or create an account straight from the API:
curl -X POST https://api.joinpluto.com/v1/checkout/free \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "name": "Acme Inc"}'The 201 response contains your tenant_id and your raw api_key — exactly once. Pluto
stores only a hash; if you lose the key, mint a replacement from the
dashboard (opens in a new tab) Settings page or /v1/api-keys.
Paid tiers: POST /v1/checkout/{hobby|pro|scale} returns a Stripe Checkout URL.
Enterprise: contact sales@joinpluto.com.
After payment, the success page redeems your key using the checkout session_id (one time only —
treat the session id as a secret).
export PLUTO_API_KEY=sk_live_...2. First call — check your quota
GET /v1/usage is a cheap read that confirms auth works and shows your plan.
curl https://api.joinpluto.com/v1/usage \
-H "Authorization: Bearer $PLUTO_API_KEY"// Plain fetch
const res = await fetch('https://api.joinpluto.com/v1/usage', {
headers: { Authorization: `Bearer ${process.env.PLUTO_API_KEY}` },
});
const usage = await res.json();// TypeScript SDK (MIT) — npm publish landing shortly; until then use REST or MCP
import { Pluto } from 'pluto-sdk-ts';
const pluto = new Pluto({ apiKey: process.env.PLUTO_API_KEY! });
const usage = await pluto.observability.usage();Default self-serve keys carry the * wildcard scope and work on every endpoint; if you minted
a deliberately-scoped key, this call requires the usage:read scope (a 403 forbidden here
means your key's scopes don't include it).
Response:
{
"period_start": "2026-07-01T00:00:00.000Z",
"period_end": "2026-08-01T00:00:00.000Z",
"tier": "free",
"actions_used": 12,
"actions_limit": 1000,
"agent_runs_used": 1,
"agent_runs_limit": 5,
"workflows_used": 0,
"workflows_limit": 0,
"cost_usd_period": 0.04,
"overage_charges_usd": 0
}3. The hero call — run an AEO scan
POST /v1/aeo/scan queues a real multi-platform scan: it asks the AI answer engines your
queries and records whether your brand shows up in the answers.
Request body:
brand(string, required) — the brand name to look for.queries(string[], required, min 1) — the questions to ask each platform.platforms(optional) — any ofchatgpt,claude,gemini,perplexity,grok,deepseek,metaai,ai_overview. Omit it to scan all eight.
curl -X POST https://api.joinpluto.com/v1/aeo/scan \
-H "Authorization: Bearer $PLUTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"brand": "Acme Plumbing",
"queries": ["best plumber in Austin", "emergency plumber near me"],
"platforms": ["chatgpt", "perplexity"]
}'// TypeScript SDK (npm publish pending)
const queued = await pluto.aeo.scan({
brand: 'Acme Plumbing',
queries: ['best plumber in Austin', 'emergency plumber near me'],
platforms: ['chatgpt', 'perplexity'],
});The scan runs asynchronously, so you get a 202 Accepted immediately:
{
"run_id": "run_...",
"status": "queued",
"agent_name": "aeo-scanner",
"platforms": ["chatgpt", "perplexity"],
"query_count": 2,
"poll_url": "/v1/agents/runs/run_..."
}Poll the run until it finishes:
curl https://api.joinpluto.com/v1/agents/runs/$RUN_ID \
-H "Authorization: Bearer $PLUTO_API_KEY"The poll response includes status (queued → running → completed or failed),
the full scan result in output once completed, cost_usd, timestamps, and
error_message when a run fails.
4. Understand your bill
Every metered request counts as 1 action against your tier's monthly quota. Two request types are additionally counted against their own quotas:
- Agent runs (like
/v1/aeo/scan) — heavier, LLM-backed operations. Free tier includes 5 per month; paid tiers scale up (Hobby 50, Pro 500, Scale 5,000; Enterprise custom). - Workflow runs — multi-step orchestrations (Pro and above).
Beyond that, each tier has a hard dollar spend cap per period; once any quota or the cap is
exhausted, billable calls return 402 quota_exceeded until the period resets or you upgrade.
Overages (where enabled by your plan) are billed at $0.01 per action, $5 per agent run, and
$100 per workflow run.
Watch your live numbers at GET /v1/usage or on the
dashboard analytics page (opens in a new tab), which also shows your
recent API calls.
5. Handle errors
Every error is a JSON envelope with a stable machine-readable code:
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded for current tier",
"retry_after": 12,
"fix_hint": "Back off and retry after the indicated seconds. Upgrade tier for higher limits.",
"docs_url": "https://docs.joinpluto.com/limits"
}
}The ones you'll meet first:
| Status | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing, malformed, invalid, revoked, or expired key. Send Authorization: Bearer sk_live_.... |
403 | forbidden | Your key's scopes don't cover this operation. Mint a key with the needed scope. |
402 | quota_exceeded | Period quota or spend cap exhausted. Check /v1/usage; wait for reset or upgrade. |
429 | rate_limited | Too many requests per minute. Honor the Retry-After header (also retry_after in the body). |
Full-time keys don't expire on a schedule — a 401 API key expired only occurs for short-lived
session keys; a revoked key fails with 401 API key revoked. See the complete
error reference for every code and status the API uses.
Next steps
- Authentication — keys, scopes, rotation.
- Error reference — every error code, rate-limit and quota semantics.
- Support — how to report a failed scan or a billing question.
- API Reference ↗ — the full interactive endpoint catalog.