> ## Documentation Index
> Fetch the complete documentation index at: https://api.simkl.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> From zero to writing user data in 5 minutes — see real data, get an access_token, read a watchlist, and mark a movie as watched.

<Note>
  **This site has three tabs** — switch via the header bar at the top of the page.
</Note>

<CardGroup cols={3}>
  <Card title="Documentation" icon="book-open">
    **You're here.** Concepts, walkthroughs, and recipes.
  </Card>

  <Card title="API Reference" icon="terminal" href="/api-reference/introduction">
    Every endpoint with parameter docs and a try-it-now playground.
  </Card>

  <Card title="Changelog" icon="clock-rotate-left" href="/changelog">
    What's new and what changed across the API and the docs.
  </Card>
</CardGroup>

By the end of this page you'll have:

* Made your first API call in **10 seconds** with no setup
* Created an app and obtained an `access_token` for a user
* Read the user's "Watching" anime watchlist
* Marked **Inception** as completed in that user's history

Total time: about 5 minutes.

<Note>
  **Before you ship to users, read [API rules](/api-rules).** It's a short page covering attribution, free-for-non-commercial terms, and the threshold where a commercial license kicks in. Skipping it is the most common reason apps get throttled or revoked.
</Note>

## 0. See real Simkl data right now

Trending data files are public CDN — no token required. Like every Simkl URL, the call still includes the standard `client_id` / `app-name` / `app-version` params plus a `User-Agent` header for consistency:

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
curl "https://data.simkl.in/discover/trending/today_100.json?client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0" \
  -H "User-Agent: my-app-name/1.0"
```

You'll get back the top 100 trending titles across Movies, TV, and Anime. These [data files](/api-reference/trending) update on a schedule and are perfect for "What's hot right now" surfaces.

The rest of this guide reads and writes **user-specific** data, which needs an `access_token` on top of the API key.

## 1. Get an API key

<Steps>
  <Step title="Create a Simkl account">
    [Sign up](https://passport.simkl.com/register/) — free.
  </Step>

  <Step title="Create an application">
    Go to [your developer settings](https://simkl.com/settings/developer/) and create a new app. You'll receive a `client_id` (and a `client_secret` for confidential clients).

    Pick an `app-name` (lowercase identifier like `plex-scrobbler`) and an `app-version` (e.g. `1.0`). These three values — `client_id`, `app-name`, `app-version` — go on **every** API request as URL parameters. See [Headers and required parameters](/conventions/headers).
  </Step>
</Steps>

## 2. Make your first authenticated catalog call

Catalog endpoints (Movies, TV, Anime metadata) need the API key but no user token. Fetch the **Game of Thrones** summary by its Simkl ID — `tv/17465` is heavily cached, so this is the cheapest endpoint to hit while you're testing:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"vesper"}}
  curl "https://api.simkl.com/tv/17465?client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0" \
    -H "User-Agent: my-app-name/1.0"
  ```

  ```js JavaScript theme={"theme":{"light":"github-light","dark":"vesper"}}
  const params = new URLSearchParams({
    client_id:     'YOUR_CLIENT_ID',
    'app-name':    'my-app-name',
    'app-version': '1.0',
  });
  const r = await fetch(`https://api.simkl.com/tv/17465?${params}`, {
    headers: { 'User-Agent': 'my-app-name/1.0' },
  });
  console.log(await r.json());
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  import requests
  r = requests.get(
      'https://api.simkl.com/tv/17465',
      params={
          'client_id':   'YOUR_CLIENT_ID',
          'app-name':    'my-app-name',
          'app-version': '1.0',
      },
      headers={'User-Agent': 'my-app-name/1.0'},
  )
  print(r.json())
  ```
</CodeGroup>

You'll get back a JSON object with the full record — `title`, `year`, `ids`, `overview`, `genres`, `ratings`, `poster`, `fanart`, `trailers`, `users_recommendations`, `similar`, and more. Summary endpoints (`/movies/:id`, `/tv/:id`, `/anime/:id`, plus the `/episodes/:id` variants) return this rich shape by default — no `?extended=full` needed. If anything errors out, [Errors and status codes](/conventions/errors) lists every code with cause / fix — `412` typically means a typo in `client_id`.

## 3. Pick an authentication flow

To touch **user-specific** data (watchlist, history, ratings), you need a user `access_token`. [AUTH V2](/api-reference/auth-v2) has two flows, chosen by what the device can do:

| Your app runs on... | Use |
| - | - |
| iOS, Android, web, desktop — anywhere a browser opens | **Authorization code + PKCE** |
| TV, console, watch, CLI tool, media-server plugin | **Device / PIN flow** |

<Note>
  **Use AUTH V2 for anything new.** The older AUTH V1 flows still work but are [being retired around April 2027](/guides/migrating-v1-to-v2), so starting there means doing this work twice.
</Note>

## 4. Get an access\_token

Both flows need a `code_verifier` / `code_challenge` pair or a device code — the short versions are below, and the full walkthroughs are linked underneath.

<Tabs>
  <Tab title="OAuth flow">
    Generate a `code_verifier` and its SHA-256 `code_challenge` ([how](/api-reference/oauth2-pkce)), then send the user to the consent page — note the host is **`simkl.com`**, not `api.simkl.com`:

    ```
    https://simkl.com/oauth2/authorize
      ?client_id=YOUR_CLIENT_ID
      &redirect_uri=YOUR_REDIRECT_URI
      &response_type=code
      &scope=media:read%20media:write
      &state=RANDOM_CSRF_TOKEN
      &code_challenge=YOUR_CODE_CHALLENGE
      &code_challenge_method=S256
    ```

    After approval Simkl redirects to `YOUR_REDIRECT_URI?code=AUTH_CODE&state=...&iss=https%3A%2F%2Fsimkl.com`. Check that `state` matches what you sent and that `iss` decodes to exactly `https://simkl.com` — [both](/api-reference/oauth2-authorization-code#receive-the-redirect), then exchange the code. This one hits **`api.simkl.com`**:

    ```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
    curl -X POST "https://api.simkl.com/oauth2/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -H "User-Agent: my-app-name/1.0" \
      --data-urlencode "grant_type=authorization_code" \
      --data-urlencode "client_id=YOUR_CLIENT_ID" \
      --data-urlencode "code=AUTH_CODE" \
      --data-urlencode "redirect_uri=YOUR_REDIRECT_URI" \
      --data-urlencode "code_verifier=YOUR_CODE_VERIFIER"
    ```

    You get back an `access_token` good for 7 days and a `refresh_token` good for 180. A **Server apps & services** registration also sends its `client_secret`; a **Mobile, desktop & browser apps** one does not ship a secret at all. See [client types](/api-reference/auth#client-types).

    Full walkthrough: [Authorization code flow](/api-reference/oauth2-authorization-code).
  </Tab>

  <Tab title="Device / PIN flow">
    Request a code:

    ```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
    curl -X POST "https://api.simkl.com/oauth2/device" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -H "User-Agent: my-app-name/1.0" \
      --data-urlencode "client_id=YOUR_CLIENT_ID" \
      --data-urlencode "scope=media:read media:write"
    ```

    Show the returned `user_code` and send the user to `verification_uri`. Then poll, respecting `interval` (5 seconds):

    ```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
    curl -X POST "https://api.simkl.com/oauth2/token" \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -H "User-Agent: my-app-name/1.0" \
      --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
      --data-urlencode "client_id=YOUR_CLIENT_ID" \
      --data-urlencode "device_code=YOUR_DEVICE_CODE"
    ```

    `authorization_pending` means keep polling. On `slow_down`, add 5 seconds and wait. When the user approves you get the tokens.

    Full walkthrough: [Device / PIN flow](/api-reference/oauth2-device).
  </Tab>
</Tabs>

<Warning>
  **Check the `scope` in the response before you rely on it.** Omitting `scope`, or misspelling it, silently gives you a read-only token that works until your first write. See [Scopes](/api-reference/oauth2-scopes).
</Warning>

## 5. Read the user's library

With the `access_token` in hand, fetch the user's "Watching" anime watchlist:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"vesper"}}
  curl "https://api.simkl.com/sync/all-items/anime/watching?extended=full&client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0" \
    -H "User-Agent: my-app-name/1.0" \
    -H "Authorization: Bearer ACCESS_TOKEN"
  ```

  ```js JavaScript theme={"theme":{"light":"github-light","dark":"vesper"}}
  const params = new URLSearchParams({
    extended:      'full',
    client_id:     'YOUR_CLIENT_ID',
    'app-name':    'my-app-name',
    'app-version': '1.0',
  });
  const r = await fetch(
    `https://api.simkl.com/sync/all-items/anime/watching?${params}`,
    {
      headers: {
        'User-Agent':    'my-app-name/1.0',
        'Authorization': `Bearer ${accessToken}`,
      },
    },
  );
  console.log(await r.json());
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  import requests
  r = requests.get(
      'https://api.simkl.com/sync/all-items/anime/watching',
      params={
          'extended':    'full',
          'client_id':   'YOUR_CLIENT_ID',
          'app-name':    'my-app-name',
          'app-version': '1.0',
      },
      headers={
          'User-Agent':    'my-app-name/1.0',
          'Authorization': f'Bearer {access_token}',
      },
  )
  print(r.json())
  ```
</CodeGroup>

<Note>
  **Per-type watchlist rules:** TV and anime accept all five statuses (`watching`, `plantowatch`, `hold`, `dropped`, `completed`). Movies skip `watching` and `hold` (single-session content). See [Watchlist statuses](/conventions/list-statuses) for the full matrix.
</Note>

## 6. Write something back

Mark **Inception** as completed in the user's library. Sending the title, year, and multiple IDs at once gives Simkl the best chance of matching the right record — handy when one of the external IDs is missing or out of date in Simkl's catalog:

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
curl -X POST "https://api.simkl.com/sync/history?client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0" \
  -H "User-Agent: my-app-name/1.0" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "movies": [{
      "title": "Inception",
      "year": 2010,
      "ids": {
        "simkl": 472214,
        "imdb":  "tt1375666",
        "tmdb":  "27205"
      }
    }]
  }'
```

`201 Created` response means the movie is now in the user's history. To undo it later, hit [`POST /sync/history/remove`](/api-reference/simkl/remove-from-history) with the same payload shape.

<Tip>
  **Send everything you have.** If you only know the IMDB ID, send just `imdb`. If you have a Simkl ID, that's the fastest match. When you have title + year + a couple of external IDs, Simkl walks the IDs in order and falls back to title/year fuzzy match — so the more you send, the more reliably the right title gets credited. Full key list: [Supported ID keys](/conventions/standard-media-objects#supported-id-keys).
</Tip>

<Tip>
  `POST /sync/history` is the right endpoint for "Mark as watched" buttons and one-shot history writes. For real-time playback tracking with progress percentages, see the [Scrobble guide](/guides/scrobble).
</Tip>

## Token reminder

**Your `access_token` expires after 7 days — plan for that from the start.** It came with a `refresh_token` valid for 180 days, and using it resets that 180-day window, so an app in regular use never has to ask the user again.

Refresh when the access token is close to expiring, or on a `401`. Only send the user back through Step 4 if the **refresh** also fails — that means they revoked your app from [Connected Apps settings](https://simkl.com/settings/connected-apps/), or the refresh token itself finally aged out.

Store both tokens, not just the access token. Full detail on [Refreshing](/api-reference/oauth2-tokens).

<Note>
  **If you are reading older [AUTH V1](/api-reference/auth-v1) material, it says tokens last five years and there is no refresh grant.** That was true of V1 and is not true here — V2 is 7 days plus refresh. Do not carry a "store it once and forget it" assumption across from a V1 integration.
</Note>

## Useful side-trips

<CardGroup cols={3}>
  <Card title="Cheapest way to resolve an IMDB ID" icon="hashtag" href="/api-reference/redirect">
    `GET /redirect?to=simkl&imdb=tt1375666` 301-redirects to the Simkl URL. Parse the ID from the path, no JSON to read.
  </Card>

  <Card title="Trending without auth" icon="fire" href="/api-reference/trending">
    Pre-built JSON for "Most Watched" surfaces — Today / Week / Month, per type. CDN-cached, free.
  </Card>

  <Card title="Calendar without auth" icon="calendar" href="/api-reference/calendar">
    Upcoming TV / anime / movie releases as static JSON. Per-month archives going back 12 months.
  </Card>
</CardGroup>

## What's next

<CardGroup cols={3}>
  <Card title="Sync guide" icon="arrows-rotate" href="/guides/sync">
    Read and write watchlists, history, and ratings — the full sync loop.
  </Card>

  <Card title="Scrobble guide" icon="play" href="/guides/scrobble">
    Real-time playback tracking with start / pause / stop / checkin.
  </Card>

  <Card title="Mark as watched" icon="circle-check" href="/guides/mark-as-watched">
    Single-call recipes that don't go through scrobble.
  </Card>

  <Card title="Search guide" icon="magnifying-glass" href="/guides/search">
    Find titles by ID, text, file name, or randomly.
  </Card>

  <Card title="Standard media objects" icon="film" href="/conventions/standard-media-objects">
    The Movie / Show / Anime / Episode shapes every endpoint speaks.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/conventions/errors">
    Every status code with cause / fix / example.
  </Card>

  <Card title="Set up authentication" icon="shield-halved" href="/api-reference/auth">
    Client type, secret, flow and scope — the decisions you make before writing auth code.
  </Card>

  <Card title="API Reference" icon="terminal" href="/api-reference/introduction">
    Every endpoint with an interactive playground.
  </Card>

  <Card title="Changelog" icon="clock-rotate-left" href="/changelog">
    What's new across endpoints, schemas, and the docs.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.