Particle for developers

Quick start

Use Particle from your own app

  • Add funds to your Agent Pool. The API pays from it, never from your Plus allowance.
  • Make a key in the chat's Settings, API keys. It is shown once; keep it secret.
  • Send it on every call: Authorization: Bearer pk_live_...
curl https://particlebuddy.com/v1/chat/completions \
  -H "Authorization: Bearer $PARTICLE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Hi!"}]}'

Base address https://particlebuddy.com/v1. Works with OpenAI client libraries: set the base URL and your key.

Chat

POST /v1/chat/completions

The OpenAI request and answer shape. Particle picks the best model for each message, so model is ignored and answers say "model": "particle". max_tokens goes up to 4096 (default 1024).

{"id":"chatcmpl-...","object":"chat.completion","model":"particle",
 "choices":[{"index":0,"message":{"role":"assistant","content":"Hello!"},
             "finish_reason":"stop"}],
 "usage":{"prompt_tokens":9,"completion_tokens":3,"total_tokens":12}}

Streaming: add "stream": true for server-sent events in OpenAI chunks, ending with data: [DONE]. Add "stream_options": {"include_usage": true} for a last usage chunk.

Agents

Start an agent run

Turn on "Allow API access" on the agent's page first. GET /v1/agents lists your agents and their ids.

curl https://particlebuddy.com/v1/agents/AGENT_ID/runs \
  -H "Authorization: Bearer $PARTICLE_KEY" -H "Content-Type: application/json" \
  -d '{"input":"Find three facts about lighthouses",
       "callback_url":"https://example.com/particle-hook"}'

curl https://particlebuddy.com/v1/runs/RUN_ID \
  -H "Authorization: Bearer $PARTICLE_KEY"

The start answers 202 with the run id. Poll GET /v1/runs/{id} every few seconds: status is queued, running, waiting_approval, done, failed or cancelled, with result and cost when it ends. Or give a callback_url (public https) and Particle POSTs the result there when the run ends.

Pricing

Pay as you go, at twice the model cost

Every call is charged from your Agent Pool at what the AI model and paid tools really cost Particle, times two. No monthly fee. Each charge shows in your pool and on the agent's page.

  • 1,000 short messages (a sentence or two each way): about $1 to $2 CAD.
  • 1,000 longer messages (a page in, a few paragraphs out): about $4 to $5 CAD.
  • An agent's web search: about 6 cents CAD each; reading a page, under a cent.

These are estimates; you pay the real cost of each call, times two. Before a call the pool must cover the most it could cost; the real, usually lower, price is charged after.

Limits and errors

Limits and errors

Each key has its own limits, set in Settings, API keys: requests a minute (default 60), agent runs a day (default 100) and the most it may spend a month in CAD.

  • 401 no key, a wrong key or a revoked key.
  • 402 the pool or the key's monthly cap cannot cover the call (insufficient_funds, key_spend_cap).
  • 403 API access is off for that agent. 404 not found.
  • 429 too many requests or runs: wait Retry-After seconds.
  • 503 maintenance, or Particle is busy: try again soon.
{"error":{"message":"Your Agent Pool cannot cover this request.",
  "type":"insufficient_funds","code":"insufficient_funds"}}

Webhooks

Check that a result came from Particle

Results sent to a web address (a task's "Send results to a web address", or a callback_url) are JSON: agent, task, title, result_text, files, images, sources, finished_at. Each carries X-Particle-Signature: t=<time>,v1=<HMAC-SHA256>, made with the agent's signing secret (agent page, Connect to Zapier, Make or n8n).

import crypto from "node:crypto";
function fromParticle(rawBody, header, secret) {
  const p = Object.fromEntries(header.split(",").map((x) => x.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
  const want = crypto.createHmac("sha256", secret)
    .update(`${p.t}.${rawBody}`).digest("hex");
  return want.length === String(p.v1).length &&
    crypto.timingSafeEqual(Buffer.from(want), Buffer.from(p.v1));
}

Particle tries three times (10 seconds each) and lists every delivery in the agent's activity.