# API reference

## beta

Push links from scripts, bots, and automations. Pull your reading list into any tool.

## Use cases

### Slack automation

Script that watches a Slack channel and archives every link your team drops - automatically saved to your dashboard.

### GitHub repo archiving

Pipe starred repos or trending repositories straight into send/links so you never lose an interesting project.

### RSS & newsletters

Connect an RSS feed or email digest and push articles you want to read later without opening the browser.

### Zapier / Make

Trigger a save from any app in your stack - new Notion page, Airtable row, or calendar event with a link.

## Authentication

All requests require an Authorization header with your API key.

```
Authorization: Bearer sml_your_key_here
```

API keys start with sml_. Generate yours in [Settings - Developer](/content/settings/index.html).

## Base URL

```
https://sendlinks.app/api/v1
```

## Rate limits

No rate limits are enforced during the beta.

Limits will be introduced before general availability. Be a good citizen - avoid hammering the API in tight loops.

## Errors

All error responses follow the same shape. Check ok first, then read error for a human-readable message and code for programmatic handling.

```
{
  "ok": false,
  "error": "Link already saved",
  "code": "DUPLICATE"
}
```

| Code | Status       | Message                                          |
|------|--------------|--------------------------------------------------|
| 401  | Unauthorized | Missing or invalid API key.                      |
| 400  | INVALID_URL  | The URL is malformed or could not be fetched.   |
| 400  | —            | Missing required field, e.g. "url" is required.|
| 409  | DUPLICATE    | Link is already saved in your account.           |
| 500  | —            | Server error - safe to retry with backoff.      |

## Endpoints

### POST/api/v1/push

**Save a link to your dashboard.**

#### Body parameters

| Parameter   | Required | Type   | Description                                    |
|-------------|----------|--------|------------------------------------------------|
| url         | Required | string | The URL to save.                             |
| note        | Optional | string | Note to attach to the link.                  |
| folderId    | Optional | string | Save into a specific folder by its ID.       |

#### Example request

```
curl -X POST https://sendlinks.app/api/v1/push -H "Authorization: Bearer sml_your_key" -H "Content-Type: application/json" -d '{"url":"https://medium.com/p/cool-article-example","note":"read this weekend"}'
```

#### Response 201 Created

```
{
  "ok": true,
  "link": {
    "id": "64f1a2b3c4d5e6f7a8b9c0d1",
    "url": "https://medium.com/p/cool-article-example",
    "title": "Cool Article Example",
    "category": "Tech",
    "createdAt": "2026-04-16T10:00:00.000Z"
  }
}
```

#### Error example 400 bad URL

```
{
  "ok": false,
  "error": "Invalid or unreachable URL",
  "code": "INVALID_URL"
}
```

### GET/api/v1/links

**Fetch your saved links with optional filters and pagination.**

#### Query parameters

| Parameter  | Type    | Description                                    |
|------------|---------|------------------------------------------------|
| limit      | number  | Results per page. Default 20, max 100.        |
| page       | number  | Page number. Default 1.                        |
| category   | string  | Filter by category e.g. Tech, Entertainment.   |
| unread     | boolean | Pass true to return only unread links.         |

#### Example request

```
curl "https://sendlinks.app/api/v1/links?limit=5&unread=true" -H "Authorization: Bearer sml_your_key"
```

#### Response

```
{
  "ok": true,
  "links": [
    {
      "id": "64f1a2b3c4d5e6f7a8b9c0d1",
      "url": "https://medium.com/p/cool-article-example",
      "title": "Cool Article Example",
      "description": "A short description...",
      "image": "https://cdn.example.com/og.png",
      "category": "Tech",
      "domain": "medium.com",
      "note": "read this weekend",
      "isPinned": false,
      "isFavourite": false,
      "isRead": false,
      "createdAt": "2026-04-16T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 5,
    "total": 42,
    "totalPages": 9
  }
}
```

## Quick start

### Node.js

**Save a link with the native fetch API - no dependencies needed.**

```
const API_KEY = process.env.SENDLINKS_API_KEY

async function saveLink(url, note) {
  const res = await fetch("https://sendlinks.app/api/v1/push", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url, note }),
  })

const data = await res.json()
  if (!data.ok) throw new Error(data.error)
  return data.link
}

// usage
saveLink("https://medium.com/p/cool-article-example", "read later")
  .then(link => console.log("Saved:", link.id))
  .catch(err => console.error(err.message))
```

### Python - Slack bot

**Save every link your team posts in a Slack channel.**

```
import os, re, requests
from slack_bolt import App

API_KEY = os.environ["SENDLINKS_API_KEY"]
app = App(token=os.environ["SLACK_BOT_TOKEN"])

URL_RE = re.compile(r"https?://[^"]+")

@app.event("message")
def on_message(event, say):
    for url in URL_RE.findall(event.get("text", "")):
        requests.post(
            "https://sendlinks.app/api/v1/push",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={"url": url, "note": f"from #{event['channel']}"},
        )

if __name__ == "__main__":
    app.start(3000)
```

---

Ready to build?

Generate your API key in settings.
