Narrative Lion

API Reference

Overview

The Narrative Lion API lets you programmatically access your notes, run searches, submit jobs, chat with AI, and more. Use it with Claude Code, Cursor, or any AI agent that speaks HTTP.

Base URL

https://api.narrativelion.com

Most read/write operations are available via GraphQL at POST /graphql. Billing, audio streaming, and file exports use dedicated REST endpoints. Chat uses SSE.

AI Agent Skill

Already using Claude Code, Cursor, Codex, or another AI agent? Install the Narrative Lion skill so your agent knows how to call this API for you.

Install (or update)
npx skills add rayjan0114/narrative-lion-skills

Auto-detects your AI tool and installs to the right location. Run the same command anytime to pull the latest version.

After install, set your API key in your shell:

export NL_API_KEY=nlk_your_api_key_here

Source: github.com/rayjan0114/narrative-lion-skills

Authentication

All API requests require an API key sent as a Bearer token in the Authorization header.

Example request
curl https://api.narrativelion.com/api/export/jobs \
  -H "Authorization: Bearer nlk_your_api_key_here"

API keys are available on the Pro and Business plans. Create one in Settings → API Keys.

Each key has a set of scopes that control what it can access. Keys are prefixed with nlk_ for easy identification.

REST Endpoints

REST endpoints handle billing, binary streaming, and file operations that are not suited for GraphQL.

GET/api/billing/usage

Get current billing period usage and credit balance.

Response
{
  "tier": "pro",
  "period": "month",
  "resetsAt": "2026-05-21T00:00:00.000Z",
  "credits": {
    "used": 20,
    "limit": 3600,
    "remaining": 3580
  },
  "wallet": {
    "balance": 0,
    "enabled": false
  }
}
GET/api/audio/:noteId/:segmentId

Stream a podcast audio segment for a note.

NameTypeRequiredDescription
noteIdstringRequiredNote ID
segmentIdstringRequiredSegment ID

Returns an audio stream (audio/mpeg).

POST/api/export/request

Request a Markdown export of all your notes. Returns a job ID for polling.

Request
curl -X POST https://api.narrativelion.com/api/export/request \
  -H "Authorization: Bearer nlk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"format": "markdown"}'
GET/api/export/jobs

List your export jobs and their status.

Response
{
  "jobs": [
    {
      "id": "job-456",
      "status": "completed",
      "format": "markdown",
      "createdAt": "2026-04-20T10:00:00.000Z"
    }
  ]
}
GET/api/export/download/:jobId

Download a completed export as a zip file.

Returns the file as application/zip.

GraphQL

The GraphQL endpoint is at POST /graphql. Send a JSON body with query and optional variables.

Basic GraphQL request
curl https://api.narrativelion.com/graphql \
  -H "Authorization: Bearer nlk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ me { id email name } }"
  }'

Queries

List jobs

query {
  jobs(status: queued, limit: 10) {
    id
    url
    status
    createdAt
  }
}

Get your notes

query {
  myNotes(limit: 20) {
    id
    videoId
    noteType
    lang
    noteMd
    tags
    updatedAt
  }
}

Get a single note

query {
  note(noteId: "abc-123") {
    id
    videoId
    noteType
    lang
    title
    noteMd
    tags
    starredAt
    createdAt
    updatedAt
  }
}

Get a note with raw transcript

query {
  note(noteId: "abc-123") {
    id
    noteMd
    transcriptAvailable
    transcriptBytes
    rawTranscript    # only fetched when selected
  }
}

rawTranscript is lazy — it only triggers a storage fetch when explicitly selected. For cheap metadata-only queries, use transcriptAvailable and transcriptBytes to check availability first. Transcripts are only available for video notes that have finished processing.

Browse notes with filters

query {
  browseNotes(noteType: "video", starred: true, sort: "updated", limit: 10) {
    id
    noteMd
    starredAt
    tags
  }
}

Search notes (semantic)

query {
  search(query: "machine learning fundamentals", limit: 5) {
    id
    videoId
    title
    noteMd
    score
  }
}

Full-text search

query {
  ftsSearch(query: "transformer architecture", limit: 10) {
    id
    videoId
    noteType
    snippet
    rank
  }
}

Mutations

Submit a YouTube video for note generation

mutation {
  submitJob(url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ") {
    id
    status
    createdAt
  }
}

Create a general note

mutation {
  createGeneralNote(content: "# My Notes\n\nSome content here...") {
    id
    noteMd
    createdAt
  }
}

Update a note

mutation {
  updateNote(noteId: "abc-123", noteMd: "# Updated content") {
    id
    version
    updatedAt
  }
}

Delete a note

mutation {
  deleteNote(noteId: "abc-123")
}

Star / unstar a note

mutation {
  toggleStar(noteId: "abc-123") {
    id
    starredAt
  }
}

Manage tags

mutation {
  addTag(noteId: "abc-123", tag: "important") {
    id
    tags
  }
}

SSE Streaming

The chat endpoint uses Server-Sent Events (SSE) for streaming responses. Send a POST request and read the event stream.

POST/api/chat/stream

Start a streaming chat session.

Request
curl -N https://api.narrativelion.com/api/chat/stream \
  -H "Authorization: Bearer nlk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "threadId": "thread-uuid",
    "actionId": "action-uuid",
    "event": {
      "type": "user_text",
      "payload": { "text": "Summarize my latest note" }
    }
  }'
NameTypeRequiredDescription
threadIdstringRequiredChat thread ID (UUID). Create a new one per conversation.
actionIdstringRequiredUnique action ID (UUID) for idempotency.
event.typestringRequired"user_text", "interview_answer", or "edit_note"
event.payloadobjectRequiredEvent-specific data. For user_text: { text: string }

Event stream format

event: token
data: {"t":"Hello"}

event: token
data: {"t":" world"}

event: complete
data: {"messageId":"msg-123","usage":{"inputTokens":150,"outputTokens":42}}
EventDescription
tokenIncremental text token from the model
completeStream finished. Includes message ID and usage stats.
errorAn error occurred during generation

Scopes

Each API key has a set of scopes that determine what operations it can perform. When creating a key, choose a preset or customize individual scopes.

ScopePresetAccess
notes:readReadRead notes, browse, get note by ID
notes:writeStandardCreate, update, star, tag notes
notes:delete-Delete notes
searchReadSemantic search and full-text search
jobs:readReadView job status
jobs:submitStandardSubmit YouTube URLs for note generation
chatStandardChat with AI (SSE streaming)
podcastStandardGenerate podcast audio
exportStandardRequest and download exports
billing:readReadView usage and credit balance

Read preset includes: notes:read, search, jobs:read, billing:read.
Standard preset includes all Read scopes plus: notes:write, jobs:submit, chat, podcast, export.

Error Codes

The API returns errors as JSON with an error field and an optional code field.

Error response
{
  "error": "Credit limit reached (120/100). Resets 2026-05-21",
  "code": "QUOTA_EXCEEDED"
}
HTTPCodeDescription
401TOKEN_MISSINGNo API key provided
401TOKEN_INVALIDInvalid or revoked API key
401TOKEN_EXPIREDAPI key has expired
403-Scope not sufficient, or plan downgraded below Pro
429RATE_LIMITEDToo many requests
402QUOTA_EXCEEDEDCredit limit reached for this billing period

Rate Limits

Rate limits are applied per account. If you exceed the limit, the API returns a 429 status.

EndpointLimit
Chat5 requests / 10 seconds
Search20 requests / 60 seconds
Mutations (write operations)30 requests / 60 seconds
Job submission10 jobs / minute per account