Quickstart

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_keyexactly 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 of chatgpt, 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 (queuedrunningcompleted 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:

StatusCodeMeaning
401unauthorizedMissing, malformed, invalid, revoked, or expired key. Send Authorization: Bearer sk_live_....
403forbiddenYour key's scopes don't cover this operation. Mint a key with the needed scope.
402quota_exceededPeriod quota or spend cap exhausted. Check /v1/usage; wait for reset or upgrade.
429rate_limitedToo 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