API
Start the same research the site runs, over HTTP, and fetch the finished report. Each search spends your account's credits at the prices on Pricing.
Get a key
Create one under Account, Connections, API. You see the full key once, so copy it when it appears.
Manage API keysQuick start
IDEMPOTENCY_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
curl -i -X POST https://last30days.dev/api/v1/search \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{"query": "AI coding agents", "depth": "default", "clarify": {"run_broadly": true}}'
# -> HTTP 202
# -> {"search_id":"<uuid>","status":"running","query":"AI coding agents"}curl "https://last30days.dev/api/v1/search?id=<search_id>&progress_cursor=0" \
-H "Authorization: Bearer sk_live_your_key_here"
# while active -> {"status":"running","progress":{"from":0,"next":12000,"delta":"...","has_more":true,"reset":false}, ...}
# when done -> {"id":"...","status":"complete","synthesis_text":"...", ...}jq -r .search_id to capture the id for step 2. Treat progress cursors as opaque; append delta in order, and replace local progress when reset is true.Your searches
curl "https://last30days.dev/api/v1/searches?limit=20" -H "Authorization: Bearer sk_live_your_key_here"
# -> {"searches":[{"id":"<uuid>","query":"AI coding agents","status":"complete", ...}],"next_before":"2026-10-01T12:00:00.123456+00:00"}
curl -G https://last30days.dev/api/v1/searches --data-urlencode "before=2026-10-01T12:00:00.123456+00:00" -H "Authorization: Bearer sk_live_your_key_here"Credits and billing
curl https://last30days.dev/api/v1/credits -H "Authorization: Bearer sk_live_your_key_here"
# -> {"credits":600,"reserved":0,"credit_debt":0,"available_credits":600, ...}curl "https://last30days.dev/api/v1/credits/activity?show=all" -H "Authorization: Bearer sk_live_your_key_here"
# -> {"activity":[{"kind":"report","at":"...","search_id":"<uuid>","query":"AI coding agents","charge":"settled","label":"170 credits", ...},
# {"kind":"purchase","at":"...","label":"Curious pack","detail":"+1,000 credits","paid_usd":10}]}Scheduled reports
Everything on the Scheduled tab works with the same key. Each scheduled run spends credits at the search prices above, and an account can have 10 active scheduled reports. Delivery windows are two hours long and set in your home time zone. Errors come back as {"error": "..."} with the status: 400 for a bad field, 401 for a missing or revoked key, 402 with requires_credits, balance and needed when your credits cannot cover one run, 403 at the active limit or for a Slack workspace you have not connected, 404 for a schedule that is not yours (DELETE answers ok either way), 422 when the schedule would have no destination or your account has no time zone, and 429 when the key is over its rate limit.
curl https://last30days.dev/api/v1/subscriptions \
-H "Authorization: Bearer sk_live_your_key_here"
# -> {"subscriptions":[{"id":"<uuid>","query":"AI coding agents","cadence":"weekly", ...}]}curl -X POST https://last30days.dev/api/v1/subscriptions \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"query": "AI coding agents", "cadence": "weekly", "delivery_weekday": 1, "delivery_hour": 8, "time_zone": "America/New_York"}'
# -> {"id":"<uuid>","next_run_at":"...","next_window_at":"...","first_delivery":null}{"status": "paused"} # or "active" to resume
{"cadence": "daily", "delivery_hour": 18} # weekly also takes delivery_weekday
{"deliver_email": false, "email_separate": true} # either field alone works too
{"slack_installation_id": "<id>", "slack_channel_id": "C123", "slack_channel_name": "news"}
{"slack_channel_id": null, "slack_channel_name": null} # stop posting to Slackcurl -X DELETE https://last30days.dev/api/v1/subscriptions/<id> -H "Authorization: Bearer sk_live_your_key_here"
curl -X POST https://last30days.dev/api/v1/subscriptions/<id>/run -H "Authorization: Bearer sk_live_your_key_here"
# -> {"search_id":"<uuid>"}curl https://last30days.dev/api/v1/account/time-zone -H "Authorization: Bearer sk_live_your_key_here"
# -> {"time_zone":"America/New_York"}
curl -X PUT https://last30days.dev/api/v1/account/time-zone \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"time_zone": "Europe/London"}'curl https://last30days.dev/api/v1/slack/channels -H "Authorization: Bearer sk_live_your_key_here"
# -> {"workspaces":[{"installation_id":"<id>","team_name":"Acme","status":"active","channels":[{"id":"C123","name":"news"}]}]}Meeting briefs
Everything on the Meetings page works with the same key. Connect Google Calendar on the site first, under Account, Connections, Google Calendar. Each brief spends credits, up to per_brief_cost_credits. Errors come back as {"error": "..."} with the status: 400 for a bad field, 401 for a missing or revoked key, 404 for a meeting or connection that is not yours, 409 when briefs are waiting for credits or a meeting cannot take that action now, and 429 when the key is over its rate limit.
curl https://last30days.dev/api/v1/briefs -H "Authorization: Bearer sk_live_your_key_here"
# -> {"connected":true,"settings":{"enabled":false,"status":"active","daily_cap":3, ...},"upcoming":[...], ...}curl -X PATCH https://last30days.dev/api/v1/briefs \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"daily_cap": 2, "internal_domains": ["acme.com", "sam@gmail.com"]}'curl -X POST https://last30days.dev/api/v1/briefs/resume -H "Authorization: Bearer sk_live_your_key_here"
# -> {"resumed":true,"rearm":"complete"} or {"resumed":false,"already_active":true}curl https://last30days.dev/api/v1/briefs/calendar -H "Authorization: Bearer sk_live_your_key_here"
# -> {"connection_id":"<uuid>","current":{"id":"primary","name":"you@acme.com"},
# "calendars":[{"id":"primary","name":"you@acme.com","kind":"primary"},{"id":"boss@acme.com","name":"Boss","kind":"person"}]}
curl -X PUT https://last30days.dev/api/v1/briefs/calendar \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"calendar_id": "boss@acme.com"}'
# -> {"calendar":{"id":"boss@acme.com","name":"Boss"},"changed":true}curl -X POST https://last30days.dev/api/v1/briefs/<id>/run -H "Authorization: Bearer sk_live_your_key_here"
curl -X POST https://last30days.dev/api/v1/briefs/<id>/schedule -H "Authorization: Bearer sk_live_your_key_here"
curl -X POST https://last30days.dev/api/v1/briefs/<id>/skip -H "Authorization: Bearer sk_live_your_key_here"
# -> {"id":"<id>","status":"research_claimed","scheduled_by":"manual","deliver":"none", ...}curl -X POST https://last30days.dev/api/v1/briefs/left-out -H "Authorization: Bearer sk_live_your_key_here" -H "Content-Type: application/json" -d '{"connection_id": "<connection_id>", "event_id": "<event_id>", "mode": "schedule"}'curl -X PUT https://last30days.dev/api/v1/briefs/<id>/targets -H "Authorization: Bearer sk_live_your_key_here" -H "Content-Type: application/json" -d '{"person": {"kind": "named", "name": "Dana Whitfield", "company": "Whitfield Capital"}, "include_company": true}'Connections
See what is connected to your account: the AI apps you approved (name, host and connected_at), your Slack workspaces (status, and billing_disconnected when the person who installed it deleted their account) and the Slack accounts you linked, and your Google Calendar (google_email and status, or connected: false). Connecting and disconnecting are done on the site, under Account, Connections:
curl https://last30days.dev/api/v1/connections -H "Authorization: Bearer sk_live_your_key_here"
# -> {"connected_apps":[{"name":"Claude","host":"claude.ai","connected_at":"..."}],
# "slack":{"workspaces":[{"installation_id":"<id>","team_name":"Acme","status":"active","billing_disconnected":false}],"linked_identities":[{"team_name":"Acme"}]},
# "google_calendar":{"connected":true,"google_email":"you@acme.com","status":"active"}}