Shiori

API Reference

Programmatic access to your Shiori links and subscriptions. Use the API to save, fetch, update, and delete links, and manage RSS subscriptions from your own scripts, apps, and integrations.

Base URL

https://www.shiori.sh

Authentication

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

Getting an API key

Open Settings in Shiori, find the API key section, and click Generate. Copy the key immediately — it won't be shown again.

Using the key

Include the key in every request:

curl https://www.shiori.sh/api/links \
  -H "Authorization: Bearer shk_your_api_key_here"

Rate Limiting

API requests are limited to 60 requests per minute per API key. Link creation is further limited to 30 per minute. When rate limited, the API returns 429 Too Many Requests with these headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed
X-RateLimit-RemainingRequests remaining in window
X-RateLimit-ResetUnix timestamp when window resets
Retry-AfterSeconds to wait before retrying

Response Format

All responses are JSON with a success boolean. On errors, an error string is included.

Success:

{
  "success": true,
  "links": [...],
  "total": 42
}

Error:

{
  "success": false,
  "error": "Invalid API key"
}

Endpoints

GET /api/links

Fetch a paginated list of your saved links.

Query parameters

ParameterTypeDefaultDescription
limitinteger25Number of links to return (max 1000)
offsetinteger0Number of links to skip
readstringallFilter by read status: all, read, or unread
sortstringnewestSort order: newest or oldest
searchstringFull-text search across title, summary, and content
tagstringFilter by tag name or tag ID
sincestringISO 8601 timestamp — only return links created at or after this time
include_contentbooleanfalseInclude full markdown content in each link object
curl "https://www.shiori.sh/api/links?limit=10&read=unread" \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "links": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "url": "https://example.com/article",
      "title": "Example Article",
      "domain": "example.com",
      "summary": "A brief AI-generated summary of the article.",
      "image_url": null,
      "status": "created",
      "source": "api",
      "created_at": "2026-02-21T12:00:00.000Z",
      "updated_at": "2026-02-21T12:00:00.000Z",
      "read_at": null,
      "deleted_at": null,
      "purged_at": null,
      "hn_url": null,
      "file_type": null,
      "file_mime_type": null,
      "notion_page_id": null,
      "author": null,
      "discoverable_feed_url": null
    }
  ],
  "total": 142
}

POST /api/links

Save a new link. The link is created immediately and processed in the background (metadata extraction, AI summary).

Request body (JSON)

FieldTypeRequiredDescription
urlstringYesThe URL to save
titlestringNoCustom title (auto-extracted if omitted)
readbooleanNoSave as already read (default: false)
created_atstringNoISO 8601 datetime to override the saved date (useful for backfilling)
curl -X POST https://www.shiori.sh/api/links \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/article"}'

Response:

{
  "success": true,
  "linkId": "550e8400-e29b-41d4-a716-446655440000"
}

If the URL was already saved, the response includes duplicate: true and the existing link is bumped to the top of your inbox.


PATCH /api/links

Bulk update links. Supports two operations: toggling read status and moving to trash.

Archive / move to inbox

FieldTypeRequiredDescription
idsstring[]YesArray of link IDs to update
readbooleanYestrue to archive, false to move back to inbox
curl -X PATCH https://www.shiori.sh/api/links \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["550e8400-e29b-41d4-a716-446655440000"], "read": true}'

Bulk trash

FieldTypeRequiredDescription
idsstring[]YesArray of link IDs to trash
deletedbooleanYesMust be true
curl -X PATCH https://www.shiori.sh/api/links \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["550e8400-e29b-41d4-a716-446655440000"], "deleted": true}'

Response (both operations):

{
  "success": true,
  "updated": 1
}

PATCH /api/links/:id

Update a single link. Supports toggling read status, editing the title/summary, and overriding the saved date.

Toggle read status

FieldTypeRequiredDescription
readbooleanYestrue to archive, false to move back to inbox
curl -X PATCH https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"read": true}'

Edit title, summary & saved date

FieldTypeRequiredDescription
titlestringYesNew title (1–500 characters)
summarystring or nullNoNew summary (max 2000 characters), null to clear
created_atstringNoISO 8601 datetime to override the saved date

You can also update created_at on its own without providing title:

curl -X PATCH https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"created_at": "2024-01-15T12:00:00Z"}'
curl -X PATCH https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "summary": "New summary text"}'

Response:

{
  "success": true,
  "message": "Link updated",
  "linkId": "550e8400-e29b-41d4-a716-446655440000"
}

Returns 409 if the link is still being processed.


DELETE /api/links/:id

Move a link to the trash. Trashed links are automatically and permanently deleted after 7 days.

curl -X DELETE https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "message": "Link deleted successfully",
  "linkId": "550e8400-e29b-41d4-a716-446655440000"
}

PATCH /api/links/:id — Restore from trash

Restore a previously deleted link from the trash.

curl -X PATCH https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"restore": true}'

Response:

{
  "success": true,
  "message": "Link restored",
  "linkId": "550e8400-e29b-41d4-a716-446655440000"
}

GET /api/links?trash=true

Fetch links that are in the trash. Supports the same limit and offset parameters as the regular list endpoint.

curl "https://www.shiori.sh/api/links?trash=true&limit=10" \
  -H "Authorization: Bearer shk_your_api_key_here"

DELETE /api/links

Permanently delete all links in the trash. This cannot be undone.

curl -X DELETE https://www.shiori.sh/api/links \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "deleted": 12
}

Subscriptions

GET /api/subscriptions

List your RSS subscriptions.

curl https://www.shiori.sh/api/subscriptions/ \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "subscriptions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "feed_url": "https://example.com/feed.xml",
      "title": "Example Blog",
      "description": "A great blog",
      "site_url": "https://example.com",
      "icon_url": "https://icons.tango.us/example.com",
      "last_synced_at": "2026-03-07T12:00:00Z"
    }
  ]
}

POST /api/subscriptions

Subscribe to an RSS or Atom feed. Accepts a website URL or direct feed URL — the feed is auto-discovered.

Request body (JSON)

FieldTypeRequiredDescription
feedUrlstringYesURL of the feed or website
initialSyncbooleanNoSync the 3 most recent items (default: false)
curl -X POST https://www.shiori.sh/api/subscriptions/ \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"feedUrl": "https://example.com/feed.xml", "initialSync": true}'

Response:

{
  "success": true,
  "subscription": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "feed_url": "https://example.com/feed.xml",
    "title": "Example Blog"
  }
}

Returns 409 if you are already subscribed to this feed.


DELETE /api/subscriptions/:id

Remove a subscription.

curl -X DELETE https://www.shiori.sh/api/subscriptions/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "deleted": true
}

POST /api/subscriptions/sync/:id

Trigger an immediate sync for a subscription. New feed items are saved as links.

Request body (JSON, optional)

FieldTypeRequiredDescription
limitintegerNoMax items to sync (1–100)
curl -X POST https://www.shiori.sh/api/subscriptions/sync/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"limit": 5}'

Response:

{
  "success": true,
  "subscriptionId": "550e8400-e29b-41d4-a716-446655440000",
  "newItems": 3,
  "skipped": 2,
  "errors": 0,
  "syncedAt": "2026-03-07T12:00:00Z"
}

Tags

GET /api/tags

List all your tags, ordered by position.

curl https://www.shiori.sh/api/tags \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "design",
      "position": 0,
      "created_at": "2026-03-10T12:00:00Z"
    }
  ]
}

POST /api/tags

Create a new tag. Names are automatically slugified (lowercased, spaces replaced with hyphens, max 16 characters).

Request body (JSON)

FieldTypeRequiredDescription
namestringYesTag name
curl -X POST https://www.shiori.sh/api/tags \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "design"}'

Response:

{
  "success": true,
  "tag": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "design",
    "position": 0,
    "created_at": "2026-03-10T12:00:00Z"
  }
}

Returns 409 if a tag with that name already exists.


PATCH /api/tags/:id

Rename a tag.

Request body (JSON)

FieldTypeRequiredDescription
namestringYesNew tag name
curl -X PATCH https://www.shiori.sh/api/tags/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "engineering"}'

Response:

{
  "success": true,
  "tag": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "engineering",
    "position": 0,
    "created_at": "2026-03-10T12:00:00Z"
  }
}

DELETE /api/tags/:id

Delete a tag. Links are untagged but not deleted.

curl -X DELETE https://www.shiori.sh/api/tags/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "deleted": true
}

PUT /api/links/:id/tags

Set the tags on a link. Replaces all existing tags with the provided list.

Request body (JSON)

FieldTypeRequiredDescription
tagIdsstring[]YesArray of tag IDs (max 10 per link)
curl -X PUT https://www.shiori.sh/api/links/550e8400-e29b-41d4-a716-446655440000/tags \
  -H "Authorization: Bearer shk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"tagIds": ["tag-id-1", "tag-id-2"]}'

Response:

{
  "success": true
}

User

GET /api/user/me

Get the authenticated user's profile and subscription info.

curl https://www.shiori.sh/api/user/me \
  -H "Authorization: Bearer shk_your_api_key_here"

Response:

{
  "success": true,
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "full_name": "Jane Doe",
    "avatar_url": "https://example.com/avatar.jpg",
    "created_at": "2025-01-15T12:00:00Z",
    "subscription": {
      "plan": "subscription",
      "is_active": true,
      "status": "active"
    }
  }
}

Error Codes

StatusDescription
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid API key
404Not found — link does not exist or not yours
409Conflict — link is still being processed
429Rate limited — too many requests
500Server error