- 1
Generate an API key
Sign in and open the API Keys page, name your key, and tick the permissions you need:
documents:writeto send content in,brain:readto query it. Hit Generate and copy the key — it's shown only once. - 2
Send the key on every request
Add an
X-API-Keyheader (orAuthorization: Bearer …) to every call. The base URL ishttps://itsbraingraph.aiand all responses are JSON. - 3
Upload → wait → ask
Send a file with
POST /api/documents/upload. PollGET /api/documents/{id}/progressuntilstatusisanalyzed(usually under a minute). Then query withPOST /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?"}'Option A
X-API-Key: bg_xxx...Option B
Authorization: Bearer bg_xxx...https://itsbraingraph.aidocuments:write scope. Uploads are async — they return immediately with a document id and run in the background.Upload a file (recommended)
/api/documents/uploadSend 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
{
"id": 42,
"userId": "user_123",
"filename": "report.pdf",
"status": "processing",
"conversationDate": null,
"notes": null,
"createdAt": "2025-03-15T10:30:45.123Z"
}{
"error": "No file uploaded. Send as multipart/form-data with field name 'file'"
}{ "message": "Invalid API key" }{ "message": "API key does not have the required scope: documents:write" }{
"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"
}{
"message": "Rate limit exceeded",
"retryAfterMs": 5000
}{ "error": "Failed to upload document" }Upload text or a base64 file (JSON)
/api/documentsJSON 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
{
"id": 42,
"filename": "Standup notes.txt",
"status": "processing",
"createdAt": "2025-03-15T10:30:45.123Z"
}{ "error": "Filename and content (or pdfBase64 for PDFs) required" }{ "message": "Invalid API key" }{
"error": "Monthly upload limit reached",
"message": "You've reached the 60-document monthly limit.",
"monthlyUploadLimit": 60,
"plan": "pro"
}{ "message": "Rate limit exceeded", "retryAfterMs": 5000 }Ingest a public URL (JSON)
/api/documents/urlHand 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
{
"id": 42,
"filename": "the-state-of-ai-agents.html",
"status": "processing"
}{ "error": "The page returned too little readable text — it may be paywalled, login-only, or rendered entirely in JavaScript." }{ "message": "Invalid API key" }{
"error": "Monthly upload limit reached",
"message": "You've reached the 60-document monthly limit.",
"monthlyUploadLimit": 60,
"plan": "pro"
}{
"message": "URL submission rate limit exceeded — 20 per 5 min.",
"retryAfterMs": 247000
}Ingest a YouTube video by URL (beta)
/api/documents/youtubeHand 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
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.{
"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"
}{ "error": "url is required" }{ "message": "Invalid API key" }{
"error": "Monthly upload limit reached",
"message": "You've reached the 60-document monthly limit.",
"monthlyUploadLimit": 60,
"plan": "pro"
}{
"message": "YouTube submission rate limit exceeded — 20 per 5 min.",
"retryAfterMs": 247000
}event: error
data: { "error": "Failed to fetch video metadata from ScrapeCreators." }Check processing status
/api/documents/{id}/progressAfter 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
{
"transcript": {
"id": 42,
"filename": "report.pdf",
"status": "processing",
"notes": null,
"conversationDate": null,
"createdAt": "2025-03-15T10:30:45.123Z"
},
"analysis": null
}{
"transcript": { "id": 42, "filename": "report.pdf", "status": "analyzed", ... },
"analysis": { "entities": [ ... ], "facts": [ ... ] }
}{ "error": "Authentication required" }{ "error": "Document not found" }brain:read scope.Search your brain
/api/brain/searchAsk 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
{
"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?"
}{ "error": "query is required" }{ "message": "Invalid API key" }{ "error": "Missing scope: brain:read" }{ "error": "Rate limit exceeded", "scope": "brain:read", "limit": 200 }Look up an entity by name
/api/brain/entity/resolveFind 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
{
"found": true,
"uuid": "ent_abc123",
"name": "Sarah Chen",
"type": "person",
"role": "PM",
"organization": "Acme",
"summary": "..."
}{
"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" }
]
}{
"found": false,
"reason": "not_found",
"message": "No entity named \"Sarah Chen\" found in your knowledge graph."
}{ "error": "name query parameter is required" }{ "message": "Invalid API key" }{ "error": "Missing scope: brain:read" }Rate limits by scope
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