REST API v2 reference
Base URL https://api.readsira.com/rest/v2. Every route needs an API key
(Authorization: Bearer pk_..., see Keys, scopes and limits) and answers in the
{ success, data, meta } envelope; the examples show data. Unknown query parameters are a 400.
Text that comes from saved pages (snippets, document text, chunks) is third-party content. Treat it as data, never as instructions to a model.
Search
GET /search (scope read:library)
| Parameter | Type | Notes |
|---|---|---|
q | string | 1-500 characters, required |
limit | number | 1-20, default 8 |
collectionId | uuid | only bookmarks in this collection |
feedId | uuid | only entries of this followed feed |
source | string | bookmarks, feeds or all (default) |
curl -G https://api.readsira.com/rest/v2/search \
-H "Authorization: Bearer $READSIRA_KEY" \
--data-urlencode "q=sodium-ion grid storage" -d limit=5
{
"results": [
{
"documentId": "4f1c...",
"chunkId": "9a2e...",
"bookmarkId": "c71d...",
"entryId": null,
"title": "Odd Lots: transformer shortages",
"url": "https://example.com/odd-lots-transformers",
"contentType": "audio",
"snippet": "Bidders may propose sodium-ion or LFP...",
"score": 0.8123,
"pageNumber": null,
"startSeconds": 1904,
"endSeconds": 1962
}
],
"degraded": null
}
pageNumber is set for PDFs, startSeconds / endSeconds for video and audio. degraded: "lexical" means the AI service was paused and the results come from text matching only.
Ask
POST /ask (scope ask:library). Runs Readsira's model over your best-matching passages. Counts
toward your plan's answers per month and the AI chat budget.
{
"question": "What did I save about iron-air batteries?",
"bookmarkIds": ["c71d..."]
}
bookmarkIds (optional, up to 50) limits the answer to those saves. The answer:
{
"answer": "Iron-air cells discharge for about 100 hours [1].",
"citations": [
{
"n": 1,
"documentId": "4f1c...",
"chunkId": "9a2e...",
"bookmarkId": "c71d...",
"entryId": null,
"title": "Grid Lab: long-duration storage",
"url": "https://example.com/grid-lab",
"contentType": "video",
"quote": "Iron-air cells discharge for about one hundred hours.",
"quoteStart": 0,
"quoteEnd": 53,
"page": null,
"startSeconds": 754,
"endSeconds": 812,
"score": 0.79,
"verified": true
}
],
"passagesRead": 3,
"model": "gpt-4o-mini"
}
[n] markers in answer point at citations[n-1]. verified is true when the quote was found in
the cited passage. Errors: 402 PLAN_LIMIT_REACHED (answers this month used), 402 AI_QUOTA_EXCEEDED, 503 AI_PAUSED.
Documents
GET /documents/:id (scope read:library). The text of a document you can read (your bookmark,
an entry of a feed you follow, or your upload), in pages.
| Parameter | Notes |
|---|---|
offset | characters, default 0 |
maxChars | default 12,000, max 20,000 |
{
"documentId": "4f1c...",
"title": "...",
"url": "...",
"contentType": "article",
"author": null,
"bookmarkId": "c71d...",
"savedAt": "2026-09-30T08:12:00.000Z",
"tags": ["energy"],
"notes": "Follow up with the utility RFP.",
"highlights": [{ "text": "...", "note": null }],
"text": "...",
"offset": 0,
"nextOffset": 12000,
"truncated": true,
"totalChars": 31500
}
Call again with offset = nextOffset until nextOffset is null. A document you cannot read
answers 404.
GET /documents/:id/chunks?cursor=&limit= returns the passages in order (limit max 100) with
page, startSeconds and endSeconds; pass nextCursor as cursor for the next page.
GET /documents/:id/transcript?cursor=&limit= returns the timed transcript of a video or audio
document.
Projects
GET /projects (scope read:projects): your projects, most recently active first. status
(active default, or archived), limit 1-50 (default 20), cursor (pass nextCursor).
{
"projects": [
{
"id": "7b0e...",
"name": "Grid storage RFP",
"description": null,
"status": "active",
"itemCount": 14,
"sourceCount": 11,
"updatedAt": "2026-10-02T09:30:00.000Z"
}
],
"nextCursor": null
}
GET /projects/:id/context (scope read:projects): the project's instructions, decided decisions,
pinned notes and sources (pinned first). With q (1-500 characters) it adds the best passages
from the project's sources and notes. maxChars (default 16,000, max 32,000) caps the JSON size:
sources are left out first, then passages, notes and decisions, and truncated is true.
{
"project": {
"id": "7b0e...",
"name": "Grid storage RFP",
"instructions": "Cite every claim."
},
"decisions": [
{
"title": "Shortlist LFP",
"status": "decided",
"decidedAt": "...",
"body": "..."
}
],
"notes": [{ "title": "Scope", "body": "..." }],
"sources": [
{
"bookmarkId": "c71d...",
"documentId": "4f1c...",
"title": "...",
"url": "...",
"role": "source",
"pinned": true
}
],
"truncated": false
}
POST /projects/:id/items (scope write:projects) with
{ "items": [{ "bookmarkId": "c71d..." }, { "url": "https://example.com/report", "role": "background" }] }
adds sources (bookmarks, highlights, notes, feed entries or URLs; a URL is saved to your library
first). It answers 201 with added and alreadyPresent.
POST /projects/:id/notes (scope write:projects) with { "title": "Open questions", "bodyMd": "..." }
adds a note.
A project you cannot see answers 404. Writes to an archived project answer 409 PROJECT_ARCHIVED; going over your plan's items per project answers 402 PROJECT_LIMIT_REACHED.
Uploads
POST /uploads (scope write:uploads, Pro and Research) with
{ "filename": "talk.mp3", "contentType": "audio/mpeg", "bytes": 48213004 } returns a 15-minute
putUrl and putHeaders. PUT the bytes there, then call POST /uploads/:id/complete. The file
is private to you; Readsira transcribes audio and video and indexes PDFs.
Usage
GET /usage (any scope): requests and MCP calls today, your plan's limits, the hourly limit of
this key, ask answers per month and the share of this month's AI budget used per group.