| name | screenpipe-api |
| description | Query the user's data via the local screenpipe REST API at localhost:3030 — screen recordings, audio, UI elements, usage analytics, and the user's persistent memory store. Use when the user asks about their screen activity, meetings, apps, productivity, media export, retranscription, connected services, OR when they ask to save / remember / store / note information so it can be retrieved later (POST /memories — survives across sessions and is queryable by Claude/external agents via the same API). |
Screenpipe API
Local REST API at http://localhost:3030.
Authentication
ALL requests require authentication. Add the auth header to every curl call:
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/..."
The $SCREENPIPE_LOCAL_API_KEY env var is already set in your environment. Without it you get 403. Endpoints that skip auth: /health, /ws/health, /audio/device/status, /connections/oauth/callback, /frames/*, /notify, /pipes/store/*.
Context Window Protection
API responses can be large. Always write curl output to a file first (curl ... -o /tmp/sp_result.json), check size (wc -c /tmp/sp_result.json), and if over 5KB read only the first 50-100 lines. Extract what you need with jq. NEVER dump full large responses into context.
1. Activity Summary — GET /activity-summary
Default broad-context call. Bundles apps, windows, key_texts, audio, edited_files, recording health, top memories, deduped screen+audio snippets, and a data_status + query_status + guidance triple.
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/activity-summary?start_time=30m%20ago&end_time=now"
Required: start_time, end_time. Optional: app_name, q (filters memories+snippets, drives query_status), include_recording|memories|snippets|guidance=false (each defaults true — disable to slim), max_snippets (8/12), max_snippet_chars (500/1200), max_memories (5/20).
data_status ∈ ok|empty_but_recording|no_capture_in_range|not_recording. Check before claiming "no activity". query_status ∈ not_requested|matched|no_query_matches. guidance.next_best_query is a ready-to-show hint when empty. Escalate to /search only for verbatim quotes / frame_ids.
2. Search — GET /search
Use when /activity-summary says ok but you need verbatim quotes, media paths, frame IDs, or a specific content match.
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/search?q=QUERY&content_type=all&limit=10&start_time=1h%20ago"
Parameters
| Parameter | Type | Required | Description |
|---|
q | string | No | Keywords. Do NOT use for audio searches — transcriptions are noisy, q filters too aggressively. |
content_type | string | No | all (default), accessibility, audio, input, ocr, memory. Screen text is primarily captured via the OS accessibility tree (accessibility); OCR is a fallback for apps without accessibility support (videos, games, remote desktops). Use all unless you need a specific modality. |
limit | integer | No | Default: 20. Keep small (≤20) to protect context. |
offset | integer | No | Pagination. Default: 0 |
start_time | ISO 8601 or relative | Yes | Accepts 2024-01-15T10:00:00Z or 16h ago, 2d ago, 30m ago |
end_time | ISO 8601 or relative | No | Defaults to now. Accepts now, 1h ago |
app_name | string | No | e.g. "Google Chrome", "Slack", "zoom.us" |
window_name | string | No | Window title substring |
speaker_name | string | No | Filter audio by speaker (case-insensitive partial) |
focused | boolean | No | Only focused windows |
max_content_length | integer | No | Truncate each result's text (middle-truncation) |
Critical Rules
- ALWAYS include
start_time — queries without time bounds WILL timeout
- Use
app_name when user mentions a specific app (this is string contains)
- "recent" = 30 min. "today" = since midnight. "yesterday" = yesterday's range
- If
/search is empty, fall back to /activity-summary and check data_status before saying "no data"
- If timeout, narrow the time range
Response Format
{
"data": [
{"type": "OCR", "content": {"frame_id": 12345, "text": "...", "timestamp": "...", "app_name": "Chrome", "window_name": "..."}},
{"type": "Audio", "content": {"chunk_id": 678, "transcription": "...", "timestamp": "...", "speaker": {"name": "John"}}},
{"type": "Input", "content": {"id": 999, "text": "Clicked 'Submit'", "timestamp": "...", "app_name": "Safari"}}
],
"pagination": {"limit": 10, "offset": 0, "total": 42}
}
3. Elements — GET /elements
Lightweight FTS search across UI elements (~100-500 bytes each vs 5-20KB from /search).
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/elements?q=Submit&start_time=1h%20ago&limit=10"
Parameters: q, frame_id, source (accessibility|ocr), role, start_time, end_time, app_name, limit, offset.
Frame Context — GET /frames/{id}/context
Returns accessibility text, parsed nodes, and extracted URLs for a frame.
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/frames/6789/context"
Common Roles (platform-specific)
Roles are not normalized across platforms. Use the correct format for the user's OS:
| Concept | macOS | Windows | Linux |
|---|
| Button | AXButton | Button | Button |
| Static text | AXStaticText | Text | Label |
| Link | AXLink | Hyperlink | Link |
| Text field | AXTextField | Edit | Entry |
| Text area | AXTextArea | Document | Text |
| Menu item | AXMenuItem | MenuItem | MenuItem |
| Checkbox | AXCheckBox | CheckBox | CheckBox |
| Group | AXGroup | Group | Group |
| Web area | AXWebArea | Pane | DocumentWeb |
| Heading | AXHeading | Header | Heading |
| Tab | AXTab | TabItem | Tab |
| List item | AXRow | ListItem | ListItem |
OCR-only roles (fallback when accessibility unavailable): line, word, block, paragraph, page
4. Frames (Screenshots) — GET /frames/{frame_id}
curl -o /tmp/frame.png "http://localhost:3030/frames/12345"
Returns raw PNG. Never fetch more than 2-3 frames per query (~1000-2000 tokens each).
5. Media Export — POST /frames/export
curl -X POST http://localhost:3030/frames/export \
-H "Content-Type: application/json" \
-d '{"start_time": "5m ago", "end_time": "now", "fps": 1.0}'
Fields: start_time, end_time (or frame_ids array), fps (default 1.0). Max 10,000 frames.
FPS guidelines: 5min→1.0, 30min→0.5, 1h→0.2, 2h+→0.1
Returns {"file_path": "...", "frame_count": N, "duration_secs": N, "file_size_bytes": N}. Show path as inline code block for playback.
Audio & ffmpeg
Audio files from search results (file_path). Common operations:
ffmpeg -y -i /path/to/audio.mp4 -q:a 2 ~/.screenpipe/exports/output.mp3
ffmpeg -y -i input.mp4 -ss 00:01:00 -to 00:05:00 -q:a 2 clip.mp3
ffmpeg -y -i input.mp4 -filter:v "setpts=0.5*PTS" -an fast.mp4
ffmpeg -y -i input.mp4 -t 10 -vf "fps=10,scale=640:-1" output.gif
Always use -y, save to ~/.screenpipe/exports/.
6. Retranscribe — POST /audio/retranscribe
curl -X POST http://localhost:3030/audio/retranscribe \
-H "Content-Type: application/json" \
-d '{"start": "1h ago", "end": "now"}'
Optional: engine (one of deepgram, screenpipe-cloud, whisper-large, whisper-large-v3-turbo, whisper-large-v3-turbo-quantized, qwen3-asr, parakeet, parakeet-mlx, openai-compatible), vocabulary (array of {"word": "...", "replacement": "..."} for bias/replacement), prompt (topic context for Whisper).
Keep ranges short (1h max). Show old vs new transcription.
7. Raw SQL — POST /raw_sql
curl -X POST http://localhost:3030/raw_sql \
-H "Content-Type: application/json" \
-d '{"query": "SELECT ... LIMIT 100"}'
Rules: Every SELECT needs LIMIT. Always filter by time. Read-only. Use datetime('now', '-24 hours') for time math.
WARNING: Do NOT use frame counts for time estimates — frames are event-driven, not fixed-interval. Use /activity-summary for screen time.
Schema
| Table | Key Columns | Time Column |
|---|
frames | app_name, window_name, browser_url, focused | timestamp |
ocr_text | text, app_name, window_name | join via frame_id |
elements | source, role, text, bounds_* | join via frame_id |
audio_transcriptions | transcription, device, speaker_id, is_input_device | timestamp |
audio_chunks | file_path | timestamp |
speakers | name, metadata | — |
ui_events | event_type, app_name, window_title, browser_url | timestamp |
accessibility | app_name, window_name, text_content, browser_url | timestamp |
meetings | meeting_app, title, attendees, detection_source | meeting_start |
memories | content, source, tags, importance | created_at |
Example Queries
SELECT app_name, COUNT(*) as frames FROM frames
WHERE timestamp > datetime('now', '-24 hours') AND app_name IS NOT NULL
GROUP BY app_name ORDER BY frames DESC LIMIT 20
SELECT CASE WHEN INSTR(SUBSTR(browser_url, INSTR(browser_url, '://') + 3), '/') > 0
THEN SUBSTR(SUBSTR(browser_url, INSTR(browser_url, '://') + 3), 1, INSTR(SUBSTR(browser_url, INSTR(browser_url, '://') + 3), '/') - 1)
ELSE SUBSTR(browser_url, INSTR(browser_url, '://') + 3) END as domain,
COUNT(*) as visits FROM frames
WHERE timestamp > datetime('now', '-24 hours') AND browser_url IS NOT NULL
GROUP BY domain ORDER BY visits DESC LIMIT 20
SELECT COALESCE(NULLIF(s.name, ''), 'Unknown') as speaker, COUNT(*) as segments
FROM audio_transcriptions at LEFT JOIN speakers s ON at.speaker_id = s.id
WHERE at.timestamp > datetime('now', '-24 hours')
GROUP BY at.speaker_id ORDER BY segments DESC LIMIT 20
SELECT strftime('%H:00', timestamp) as hour, COUNT(*) as switches
FROM ui_events WHERE event_type = 'app_switch' AND timestamp > datetime('now', '-24 hours')
GROUP BY hour ORDER BY hour LIMIT 24
Common patterns: GROUP BY date(timestamp) (daily), GROUP BY strftime('%H:00', timestamp) (hourly), HAVING frames > 5 (filter noise).
8. Connections — GET /connections
curl http://localhost:3030/connections
curl http://localhost:3030/connections/telegram
Returns credentials to use with service APIs directly:
- Telegram:
bot_token + chat_id → POST https://api.telegram.org/bot{token}/sendMessage
- Slack:
webhook_url → POST {webhook_url} with {"text": "..."}
- Discord:
webhook_url → POST {webhook_url} with {"content": "..."}
- Todoist:
api_token → POST https://api.todoist.com/api/v1/tasks with Bearer auth
- Teams:
webhook_url → POST {webhook_url} with {"text": "..."}
- Email:
smtp_host, smtp_port, smtp_user, smtp_pass, from_address
If not connected, tell user to set up in Settings > Connections.
Each entry's description field is self-describing — for capabilities that
need a control surface (browsers, gateways, etc.), the description includes
the exact endpoint and body shape. Read it before guessing.
Browser Control — owned-default
You have an embedded browser you can drive directly, for the user it will displayed inside the chat. Three intent verbs;
reach for /eval only when the first two aren't enough.
curl -X POST -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://en.wikipedia.org/wiki/Giraffe"}' \
http://localhost:3030/connections/browsers/owned-default/navigate
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" \
http://localhost:3030/connections/browsers/owned-default/snapshot
curl -X POST -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "return [...document.querySelectorAll(\".title > a\")].slice(0,5).map(a => a.innerText)"}' \
http://localhost:3030/connections/browsers/owned-default/eval
Default rule: try snapshot first. Switch to eval only when you need a
specific value the snapshot tree doesn't surface.
Cookies persist across calls (own profile, isolated from the user's
real browser). Password fields are stripped from snapshot output.
9. Meetings — GET /meetings
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/meetings?start_time=1d%20ago&end_time=now&limit=10&offset=0"
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/meetings/42"
Returns detected meetings (from calendar, app detection, window titles, UI elements, multi-speaker audio).
| Field | Type | Description |
|---|
id | integer | Meeting ID |
meeting_start | ISO 8601 | Start time |
meeting_end | ISO 8601? | End time (null if ongoing) |
meeting_app | string | App (zoom, teams, meet, etc.) |
title | string? | Meeting title |
attendees | string? | Attendees |
detection_source | string | How detected (app, calendar, ui, etc.) |
Also available via raw SQL: SELECT * FROM meetings WHERE meeting_start > datetime('now', '-24 hours') LIMIT 20
10. Speakers — Management & Reassignment
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/speakers/search?name=John"
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/speakers/unnamed?limit=20&offset=0"
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/speakers/similar?speaker_id=29&limit=5"
curl -X POST http://localhost:3030/speakers/update \
-H "Content-Type: application/json" \
-d '{"id": 29, "name": "Jordan"}'
curl -X POST http://localhost:3030/speakers/reassign \
-H "Content-Type: application/json" \
-d '{"audio_chunk_id": 456, "new_speaker_name": "Jordan", "propagate_similar": true}'
curl -X POST http://localhost:3030/speakers/undo-reassign \
-H "Content-Type: application/json" \
-d '{"old_assignments": [{"transcription_id": 1, "old_speaker_id": 29}]}'
curl -X POST http://localhost:3030/speakers/merge \
-H "Content-Type: application/json" \
-d '{"speaker_to_keep_id": 5, "speaker_to_merge_id": 29}'
curl -X POST http://localhost:3030/speakers/hallucination \
-H "Content-Type: application/json" \
-d '{"speaker_id": 29}'
curl -X POST http://localhost:3030/speakers/delete \
-H "Content-Type: application/json" \
-d '{"id": 29}'
Speaker Reassignment Workflow
When the user says "that was actually Jordan, not Karishma":
- Search audio results to find the
chunk_id for the misidentified audio
- Call
POST /speakers/reassign with audio_chunk_id and new_speaker_name
- With
propagate_similar: true (default), it also fixes similar-sounding chunks
11. Memories — High-Signal Persistent Knowledge
Memories are the highest-signal data source in screenpipe. They contain curated facts, user preferences, decisions, and project context — distilled from hours of screen/audio data. Always check memories when answering questions or building context.
When to Query Memories
Query memories FIRST (before or alongside /search) when:
- The user asks about preferences, decisions, or past context
- You need background on a project, person, or workflow
- You're generating a summary, recommendation, or action plan
- You're unsure about user preferences or past decisions
- Any task where historical context would improve the output
Rule: If you're calling /search, also call /memories in parallel. Memories provide the "why" behind the raw screen data. Search gives you what happened; memories tell you what matters.
API
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/memories?q=preference&limit=20"
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/memories?min_importance=0.5&limit=20"
curl -H "Authorization: Bearer $SCREENPIPE_LOCAL_API_KEY" "http://localhost:3030/memories?source=user&tags=project&limit=20"
curl -X POST http://localhost:3030/memories \
-H "Content-Type: application/json" \
-d '{"content": "User prefers dark mode", "source": "user", "tags": ["preference", "ui"], "importance": 0.7}'
curl -X PUT http://localhost:3030/memories/1 \
-H "Content-Type: application/json" \
-d '{"content": "User prefers dark mode in all apps", "importance": 0.8}'
curl -X DELETE http://localhost:3030/memories/1
Parameters for GET /memories: q (FTS search), source, tags, min_importance, start_time, end_time, limit, offset.
Use /memories directly — /search does not currently surface content_type=memory results.
Creating Memories
When you learn something important about the user (preferences, decisions, project context), store it as a memory. Use importance 0.0-1.0 to rank signal. Only store genuinely useful long-lived facts, not transient observations.
12. Notifications — POST http://localhost:11435/notify
Send a notification to the screenpipe desktop UI. This uses the Tauri sidecar server (port 11435), not the main API (port 3030).
The notification body supports markdown: **bold**, `inline code`, and [link text](url). Links can be web URLs, file paths, or screenpipe deeplinks.
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "3 new voice memos", "body": "found recordings from today"}'
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "Meeting summary", "body": "**Q3 Planning** notes saved\n\nopen [meeting notes](~/Documents/notes/q3.md) or view [recording](screenpipe://timeline)"}'
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "Export complete", "body": "saved to [report.csv](~/Downloads/report.csv)"}'
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "Meeting summary", "body": "**Q3 Planning**\n- Budget approved", "actions": [{"id": "view", "label": "view", "type": "deeplink", "url": "screenpipe://timeline"}, {"id": "skip", "label": "skip", "type": "dismiss"}]}'
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "PR ready for review", "body": "nice work", "actions": [{"id": "open", "label": "open pr", "type": "link", "url": "https://github.com/screenpipe/screenpipe/pull/1234"}]}'
curl -X POST http://localhost:11435/notify \
-H "Content-Type: application/json" \
-d '{"title": "Saved", "body": "Note saved", "timeout": 5000}'
| Field | Type | Required | Description |
|---|
title | string | Yes | Notification title |
body | string | Yes | Markdown body (**bold**, `code`, [text](url)) |
type | string | No | Category (default "pipe") |
timeout | integer | No | Auto-dismiss in ms (default 20000) |
autoDismissMs | integer | No | Alias for timeout |
actions | array | No | Action buttons |
Supported link types in body markdown:
- Web URLs:
[docs](https://docs.screenpi.pe) — opens in browser
- File paths:
[notes](~/notes/file.md) or [log](/var/log/app.log) — opens in default app
- Deeplinks:
[timeline](screenpipe://timeline) — navigates within screenpipe
Returns {"success": true, "message": "Notification sent successfully"}.
13. Other Endpoints
curl http://localhost:3030/health
curl http://localhost:3030/audio/list
curl http://localhost:3030/vision/list
Deep Links
Reference specific moments with clickable links:
[10:30 AM — Chrome](screenpipe://frame/12345) # screen text results (use frame_id)
[meeting at 3pm](screenpipe://timeline?timestamp=ISO8601) # Audio results (use timestamp)
Only use IDs/timestamps from actual search results. Never fabricate.
Showing Videos
Show file_path from search results as inline code for playable video:
`/Users/name/.screenpipe/data/monitor_1_2024-01-15_10-30-00.mp4`