API Documentation

Send content into your Brain Graph and query what you've captured — programmatically. Below is every public endpoint, with example requests and responses.

To generate an API key you need an account. Start free or log in and head to the API Keys page to mint one.

Prefer no-code? The Zapier integration wires these same endpoints to Google Drive, YouTube, Krisp, and tl;dv for you.

Quick start
Three steps from "I have an account" to "I'm querying my brain".
  1. 1

    Generate an API key

    Sign in and open the API Keys page, name your key, and tick the permissions you need: documents:write to send content in, brain:read to query it. Hit Generate and copy the key — it's shown only once.

  2. 2

    Send the key on every request

    Add an X-API-Key header (or Authorization: Bearer …) to every call. The base URL is https://itsbraingraph.ai and all responses are JSON.

  3. 3

    Upload → wait → ask

    Send a file with POST /api/documents/upload. Poll GET /api/documents/{id}/progress until status is analyzed (usually under a minute). Then query with POST /api/brain/search.

# 1) Upload a file
curl -X POST https://itsbraingraph.ai/api/documents/upload \
  -H "X-API-Key: bg_your_key_here" \
  -F "file=@report.pdf"
# → { "id": 42, "status": "processing", ... }

# 2) Poll until processing finishes (usually under a minute)
curl https://itsbraingraph.ai/api/documents/42/progress \
  -H "X-API-Key: bg_your_key_here"
# → { "transcript": { "status": "analyzed", ... }, "analysis": { ... } }

# 3) Ask a question
curl -X POST https://itsbraingraph.ai/api/brain/search \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query":"what did I learn from the Q1 report?"}'
Authentication
Every request must carry your API key. Pick either header — both are accepted.

Option A

X-API-Key: bg_xxx...

Option B

Authorization: Bearer bg_xxx...
Base URL: https://itsbraingraph.ai
Responses are JSON
Send content into your brain
Every endpoint below needs a key with the documents:write scope. Uploads are async — they return immediately with a document id and run in the background.

Upload a file (recommended)

POST
/api/documents/upload
documents:write

Send a PDF, DOCX, PPTX, TXT, MD, HTML, or EML file using multipart/form-data. This is the simplest path for automation tools (Zapier, Make, n8n) and CLI scripts — no base64 encoding required.

Parameters

file — Required. The file to upload (multipart file field). Up to ~35 MB.

filename — Optional. Override the document display name.

notes — Optional. Free-text context for the extraction step.

conversationDate — Optional. ISO 8601 date (e.g. 2025-03-15) — anchors entities on your timeline.

Request

curl -X POST https://itsbraingraph.ai/api/documents/upload \
  -H "X-API-Key: bg_your_key_here" \
  -F "file=@report.pdf" \
  -F "conversationDate=2025-03-15" \
  -F "notes=Q1 board pre-read"

Responses

201
Created— Upload accepted and queued for processing.
{
  "id": 42,
  "userId": "user_123",
  "filename": "report.pdf",
  "status": "processing",
  "conversationDate": null,
  "notes": null,
  "createdAt": "2025-03-15T10:30:45.123Z"
}
400
Bad Request— Multipart request missing the "file" field.
{
  "error": "No file uploaded. Send as multipart/form-data with field name 'file'"
}
401
Unauthorized— API key missing, invalid, revoked, or expired.
{ "message": "Invalid API key" }
403
Forbidden — Missing scope— Key is valid but lacks documents:write.
{ "message": "API key does not have the required scope: documents:write" }
403
Forbidden — Upload cap— You've hit your plan's upload cap. Free is a 40-document lifetime cap; Pro/Max is 60/month.
{
  "error": "Monthly upload limit reached",
  "message": "You've reached the 60-document monthly limit. Resets on 2026-05-15T00:00:00.000Z.",
  "monthlyUploadLimit": 60,
  "monthlyUploadResetAt": "2026-05-15T00:00:00.000Z",
  "plan": "pro"
}
429
Too Many Requests— Per-key rate limit hit. Response carries Retry-After and X-RateLimit-* headers.
{
  "message": "Rate limit exceeded",
  "retryAfterMs": 5000
}
500
Server Error— Unexpected failure during upload.
{ "error": "Failed to upload document" }

Upload text or a base64 file (JSON)

POST
/api/documents
documents:write

JSON alternative to the multipart endpoint above — useful when you can't send a file directly (e.g. some no-code platforms). Send plain text in content, or a base64-encoded file in pdfBase64.

Parameters

filename — Required. Display name for the document. Include the extension when uploading a binary file.

content — Required for text uploads. The text body. Escape newlines as \n.

isPdf — Required for binary uploads. Set to true whenever pdfBase64 is present.

pdfBase64 — Required for binary uploads. Base64-encoded file bytes (up to ~35 MB). data: URI prefixes are stripped automatically.

fileType — Optional. One of pdf, docx, pptx, eml, html, txt, md. Helps the extractor when the filename has no extension.

conversationDate — Optional. ISO 8601 date — anchors entities on your timeline.

notes — Optional. Free-text context for the extraction step.

Request

# Plain text
curl -X POST https://itsbraingraph.ai/api/documents \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "Standup notes.txt",
    "content": "Alice: I finished the auth flow.\nBob: Starting on the dashboard.",
    "conversationDate": "2025-03-15"
  }'

# Binary file (PDF/DOCX/PPTX/EML/HTML)
curl -X POST https://itsbraingraph.ai/api/documents \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "filename": "Q1 Report.pdf",
    "isPdf": true,
    "pdfBase64": "JVBERi0xLjQKMSAwIG9iago8P..."
  }'

Responses

201
Created— Document accepted and queued for processing.
{
  "id": 42,
  "filename": "Standup notes.txt",
  "status": "processing",
  "createdAt": "2025-03-15T10:30:45.123Z"
}
400
Bad Request— filename and either content or pdfBase64 are required.
{ "error": "Filename and content (or pdfBase64 for PDFs) required" }
401
Unauthorized— API key missing, invalid, revoked, or expired.
{ "message": "Invalid API key" }
403
Forbidden — Missing scope or upload cap— Either the key lacks documents:write, or you have hit your plan’s upload cap (Free: 40 lifetime; Pro/Max: 60 / month).
{
  "error": "Monthly upload limit reached",
  "message": "You've reached the 60-document monthly limit.",
  "monthlyUploadLimit": 60,
  "plan": "pro"
}
429
Too Many Requests— Per-key rate limit hit. Retry-After and X-RateLimit-* headers are returned.
{ "message": "Rate limit exceeded", "retryAfterMs": 5000 }

Ingest a public URL (JSON)

POST
/api/documents/url
documents:write

Hand the API a public web link — an article, blog post, or docs page — and the server fetches it, extracts the main body (skipping nav, ads, and comments), and ingests it like an HTML document. Paywalled, login-only, and JS-only pages are rejected without consuming an upload slot. Submissions are also capped per user (20 / 5 min) to bound outbound fetch work — see 429 below. Returns immediately with a document id; processing runs in the background.

Parameters

url — Required. A public http(s) URL. The server resolves and re-validates each redirect hop and refuses private / loopback / metadata-service addresses.

notes — Optional. Free-text context for the extraction step. The source URL is always recorded automatically.

conversationDate — Optional. ISO 8601 date — anchors entities on your timeline.

Request

curl -X POST https://itsbraingraph.ai/api/documents/url \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/blog/the-state-of-ai-agents",
    "notes": "Competitor positioning research"
  }'

Responses

201
Created— URL fetched, audited, and queued for processing.
{
  "id": 42,
  "filename": "the-state-of-ai-agents.html",
  "status": "processing"
}
400
Bad Request — missing or unusable URL— The url field was missing, or the page failed one of the fetch audits (non-200, error/login page, paywall stub, empty article, or under the minimum text length).
{ "error": "The page returned too little readable text — it may be paywalled, login-only, or rendered entirely in JavaScript." }
401
Unauthorized— API key missing, invalid, revoked, or expired.
{ "message": "Invalid API key" }
403
Forbidden — Missing scope or upload cap— Either the key lacks documents:write, or you have hit your plan’s upload cap (Free: 40 lifetime; Pro/Max: 60 / month). A rejected URL never consumes a slot.
{
  "error": "Monthly upload limit reached",
  "message": "You've reached the 60-document monthly limit.",
  "monthlyUploadLimit": 60,
  "plan": "pro"
}
429
Too Many Requests— Per-key rate limit OR per-user URL-submission rate limit (20 / 5 min) hit. Retry-After is returned.
{
  "message": "URL submission rate limit exceeded — 20 per 5 min.",
  "retryAfterMs": 247000
}

Ingest a YouTube video by URL (beta)

POST
/api/documents/youtube
documents:write

Hand the server a YouTube video URL — it fetches the video's metadata and transcript via ScrapeCreators, resolves the speakers (channel-host memory + signal voting), creates an EventSeries node for the channel on first sighting, and ingests the result like any other document. Each video is processed at most once per user and counts against your plan's upload cap (Free: lifetime; Pro/Max: monthly). Auth: a Brain Graph browser session, an API key with documents:write scope, or the OAuth Bearer token issued to the Claude MCP connector — same surface as POST /api/documents/url. The easiest way to call it is the connector's upload_youtube_url tool.

Parameters

url — Required. Full YouTube video URL. Accepts youtube.com/watch?v=, youtu.be/, shorts/, embed/, and live/ forms.

language — Optional. 2-letter caption-track language code (e.g. "en"). Defaults to whichever language ScrapeCreators selects.

Request

# The endpoint streams Server-Sent Events on fresh ingestion.
# Auth: X-API-Key (documents:write scope), browser session cookie,
# or OAuth Bearer (MCP connector). The easiest path is the MCP
# connector tool 'upload_youtube_url'. Direct curl with an API key:

curl -N -X POST https://itsbraingraph.ai/api/documents/youtube \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream, application/json" \
  -d '{
    "url": "https://www.youtube.com/watch?v=Wia0oSOu7ZQ",
    "language": "en"
  }'

Responses

200
OK — SSE stream (fresh ingest)— Content-Type is text/event-stream. The server emits a sequence of named events as the pipeline progresses; ingestion continues in the background even if the caller disconnects after persisted/queued.
event: connected
data: {"videoId":"Wia0oSOu7ZQ","canonicalUrl":"https://www.youtube.com/watch?v=Wia0oSOu7ZQ"}

event: fetching_youtube
data: { ... }

event: resolving_speakers
data: { ... }

event: speakers_resolved
data: {
  "method": "channel_memory+signals",
  "proposals": [
    { "label": "Speaker 2", "name": "Alex Hormozi", "role": "host", "confidence": 0.94 }
  ],
  "signals": [ ... ]
}

# transcript_created carries the transcriptId + the file's name.
# 'queued' here is a boolean that mirrors whether another upload is
# already ingesting (true) or this one will run inline (false).
event: transcript_created
data: { "transcriptId": 712, "filename": "...Wia0oSOu7ZQ.txt", "queued": false }

# persisted carries the youtube_sources row id + speaker resolution
# status — these are NOT on transcript_created.
event: persisted
data: { "youtubeSourceId": 5, "resolutionStatus": "pending" }

# When another upload is already ingesting, the handler emits 'queued'
# (with transcriptId again) and ends the stream; the sidecar picks the
# job up later. Otherwise progress / complete events stream from the
# sidecar inline.
event: queued
data: { "transcriptId": 712, "status": "queued", "message": "Queued — will be processed when current ingestion completes." }

# 'error' is emitted on failure;
# 'duplicate' (with transcriptId + youtubeSourceId) on dedup-hit retry path.
200
OK — already in your brain (JSON)— When the URL was previously ingested, the server returns a plain JSON body (no SSE stream) with the prior record. One-URL-once dedup.
{
  "status": "exists",
  "message": "This video is already in your brain.",
  "videoId": "Wia0oSOu7ZQ",
  "transcriptId": 712,
  "youtubeSourceId": 5,
  "title": "The Only Way to Learn Skills That Matter",
  "channelName": "MoreMozi",
  "publishedAt": "2026-05-15T01:00:22.000Z",
  "createdAt": "2026-05-15T06:30:34.100Z"
}
400
Bad Request— The url field was missing or not a recognizable YouTube URL.
{ "error": "url is required" }
401
Unauthorized— API key missing/invalid/revoked, OR no valid Brain Graph session cookie, OR no valid OAuth Bearer token.
{ "message": "Invalid API key" }
403
Forbidden — Upload cap reached— Your plan's upload cap is exhausted (Free: lifetime; Pro/Max: current month). No slot is consumed on a rejected request.
{
  "error": "Monthly upload limit reached",
  "message": "You've reached the 60-document monthly limit.",
  "monthlyUploadLimit": 60,
  "plan": "pro"
}
429
Too Many Requests— Per-user YouTube submission rate limit (20 / 5 min). Retry-After is returned.
{
  "message": "YouTube submission rate limit exceeded — 20 per 5 min.",
  "retryAfterMs": 247000
}
500
Server Error (SSE)— Emitted as event: error inside the SSE stream when the upstream ScrapeCreators fetch or the ingestion pipeline fails after headers have already flushed.
event: error
data: { "error": "Failed to fetch video metadata from ScrapeCreators." }

Check processing status

GET
/api/documents/{id}/progress
documents:write

After an upload, poll this endpoint with the document id you received. Status moves through queued → processing → syncing → analyzed. Once it reaches analyzed, the document is fully searchable and the analysis field is populated with the extracted entities and facts.

Parameters

id — Required (path). The document id returned by an upload call.

Request

curl https://itsbraingraph.ai/api/documents/42/progress \
  -H "X-API-Key: bg_your_key_here"

Responses

200
OK — still processing— Document is queued, processing, or syncing. Keep polling every few seconds.
{
  "transcript": {
    "id": 42,
    "filename": "report.pdf",
    "status": "processing",
    "notes": null,
    "conversationDate": null,
    "createdAt": "2025-03-15T10:30:45.123Z"
  },
  "analysis": null
}
200
OK — analyzed— Processing complete. The document is now in your brain and the analysis is attached.
{
  "transcript": { "id": 42, "filename": "report.pdf", "status": "analyzed", ... },
  "analysis": { "entities": [ ... ], "facts": [ ... ] }
}
401
Unauthorized— Missing or invalid API key.
{ "error": "Authentication required" }
404
Not Found— No document with that id exists for your account.
{ "error": "Document not found" }
Read from your brain
These endpoints need a key with the brain:read scope.

Search your brain

POST
/api/brain/search
brain:read

Ask a natural-language question across everything you've uploaded. Returns matching facts with relevance scores plus the source and target entities involved. Use this whenever you want an answer grounded in your captured documents.

Parameters

query — Required. Your question in plain English.

numResults — Optional. Results to return (default 10, max 50).

Request

curl -X POST https://itsbraingraph.ai/api/brain/search \
  -H "X-API-Key: bg_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "what did I promise Sarah last week?",
    "numResults": 10
  }'

Responses

200
OK— Search completed successfully.
{
  "results": [
    {
      "fact": "Alice promised Bob a follow-up on March 15",
      "score": 0.87,
      "sourceEntity": { "name": "Alice", "type": "person" },
      "targetEntity": { "name": "Bob", "type": "person" },
      "date": "2025-03-15T00:00:00Z"
    }
  ],
  "totalResults": 1,
  "query": "what did I promise Sarah last week?"
}
400
Bad Request— Missing or non-string "query" field.
{ "error": "query is required" }
401
Unauthorized— API key missing, invalid, revoked, or expired.
{ "message": "Invalid API key" }
403
Forbidden— Key lacks brain:read scope.
{ "error": "Missing scope: brain:read" }
429
Too Many Requests— Per-scope rate limit hit (200/hr). Response includes X-RateLimit-* headers.
{ "error": "Rate limit exceeded", "scope": "brain:read", "limit": 200 }

Look up an entity by name

GET
/api/brain/entity/resolve
brain:read

Find a specific person, company, topic, or project by exact name. Returns the canonical entity with its uuid and metadata. Useful as a first step when you need a stable id before calling another endpoint, or for disambiguating two people with similar names.

Parameters

name — Required (query string). The entity name. URL-encode spaces and special characters.

Request

curl "https://itsbraingraph.ai/api/brain/entity/resolve?name=Sarah%20Chen" \
  -H "X-API-Key: bg_your_key_here"

Responses

200
OK — Found— Exactly one entity matches the name.
{
  "found": true,
  "uuid": "ent_abc123",
  "name": "Sarah Chen",
  "type": "person",
  "role": "PM",
  "organization": "Acme",
  "summary": "..."
}
200
OK — Ambiguous— Several entities share that name. Re-prompt your user with the matches list.
{
  "found": false,
  "reason": "ambiguous",
  "message": "Multiple entities matching \"Sarah\" found.",
  "matches": [
    { "uuid": "ent_1", "name": "Sarah Chen", "type": "person" },
    { "uuid": "ent_2", "name": "Sarah Johnson", "type": "person" }
  ]
}
200
OK — Not found— No entity matches that name yet. Upload a document mentioning them first.
{
  "found": false,
  "reason": "not_found",
  "message": "No entity named \"Sarah Chen\" found in your knowledge graph."
}
400
Bad Request— The name query parameter is missing.
{ "error": "name query parameter is required" }
401
Unauthorized— API key missing, invalid, revoked, or expired.
{ "message": "Invalid API key" }
403
Forbidden— Key lacks brain:read scope.
{ "error": "Missing scope: brain:read" }

Rate limits by scope

documents:write
100 req/hr
brain:read
200 req/hr

Upload caps

Free: 40 documents lifetime. Pro and Max: 60 documents per calendar month. The 403 response on upload tells you when you've hit the cap and when it resets.

Status codes you'll see

200/201 — success

400 — bad request body

401 — missing or invalid API key

403 — missing scope or upload cap hit

404 — document not found

429 — rate limit hit (see Retry-After)

500 — server error, retry shortly