# Discover API

> Discover lists movies at GET /3/discover/movie and TV shows at GET /3/discover/tv, filtered and sorted by the parameters you choose: genre, cast, crew, company, keyword, dates, rating, votes, runtime, language and streaming provider. The JSON matches TMDB v3, and results are cached for 30 minutes.

## Endpoint

`GET /3/discover/movie`

```bash
curl "https://api.amodb.dev/3/discover/movie?with_genres=878&sort_by=vote_average.desc&vote_count.gte=1000" \
  -H "Authorization: Bearer $AMODB_KEY"
```

Response:

```json
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/wLWvHcQz7N13DvSOTV7bHgwGXpT.jpg",
      "genre_ids": [
        878,
        12
      ],
      "id": 687163,
      "original_language": "en",
      "original_title": "Project Hail Mary",
      "overview": "Science teacher Ryland Grace wakes up on a spaceship light years from home with no recollection of who he is or how he got there. As his memory returns, he begins to uncover his mission: solve the riddle of the mysterious substance causing...",
      "popularity": 101.0341,
      "poster_path": "/yihdXomYb5kTeSivtFndMy5iDmf.jpg",
      "release_date": "2026-03-15",
      "title": "Project Hail Mary",
      "video": false,
      "vote_average": 8.635,
      "vote_count": 8354
    },
    {
      "adult": false,
      "backdrop_path": "/Ys8UIGWJpd2TMuQk8fU77W3mBz.jpg",
      "genre_ids": [
        12,
        18,
        878
      ],
      "id": 157336,
      "original_language": "en",
      "original_title": "Interstellar",
      "overview": "The adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.",
      "popularity": 74.3949,
      "poster_path": "/yQvGrMoipbRoddT0ZR8tPoR7NfX.jpg",
      "release_date": "2014-11-04",
      "title": "Interstellar",
      "video": false,
      "vote_average": 8.491,
      "vote_count": 41526
    },
    {
      "adult": false,
      "backdrop_path": "/tanwf7KOwQ6dxZzehKsXkffIJOd.jpg",
      "genre_ids": [
        878,
        28,
        12
      ],
      "id": 969681,
      "original_language": "en",
      "original_title": "Spider-Man: Brand New Day",
      "overview": "Fighting crime full-time as Spider-Man in a world that doesn't remember him—and the pressure of seeing his old friends move on without him—sparks a change in Peter Parker he may not have the power to control. But that transformation might a...",
      "popularity": 663.5004,
      "poster_path": "/bjiS5ipwxb9JFy3XRRN4OAilSeX.jpg",
      "release_date": "2026-07-29",
      "title": "Spider-Man: Brand New Day",
      "video": false,
      "vote_average": 8.468,
      "vote_count": 5684
    }
  ],
  "total_pages": 39,
  "total_results": 772
}
```

## Discover movies

`GET /3/discover/movie` returns a paged list of movies that match every filter you send, in the order you choose. It is the endpoint for browse pages, "best of" lists and recommendations by facet, and it needs no `query` text. When you have a title typed by a user, use [search](https://amodb.dev/search/) instead.

The example lists science fiction movies (genre id `878`) with at least 1000 votes, best rated first.

```bash
curl -sSf --get "https://api.amodb.dev/3/discover/movie" \
  --data-urlencode "with_genres=878" \
  --data-urlencode "sort_by=vote_average.desc" \
  --data-urlencode "vote_count.gte=1000" \
  --data-urlencode "language=en-US" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const params = new URLSearchParams({
  with_genres: '878',
  sort_by: 'vote_average.desc',
  'vote_count.gte': '1000',
  language: 'en-US',
});
const res = await fetch(`https://api.amodb.dev/3/discover/movie?${params}`, {
  headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` },
});
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { results, total_pages } = await res.json();
console.log(`page 1 of ${total_pages}`);
for (const m of results.slice(0, 5)) console.log(m.vote_average, m.title, m.release_date);
```

```python
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/discover/movie",
    params={
        "with_genres": 878,
        "sort_by": "vote_average.desc",
        "vote_count.gte": 1000,
        "language": "en-US",
    },
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
data = r.json()
print("page 1 of", data["total_pages"])
for m in data["results"][:5]:
    print(m["vote_average"], m["title"], m["release_date"])
```

```php
<?php
$url = 'https://api.amodb.dev/3/discover/movie?' . http_build_query([
    'with_genres' => 878,
    'sort_by' => 'vote_average.desc',
    'vote_count.gte' => 1000,
    'language' => 'en-US',
]);
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
    throw new RuntimeException("amodb returned $status");
}
$data = json_decode($body, true);
foreach (array_slice($data['results'], 0, 5) as $m) {
    echo $m['vote_average'], ' ', $m['title'], PHP_EOL;
}
```

```go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"time"
)

type Page struct {
	TotalPages int `json:"total_pages"`
	Results    []struct {
		Title       string  `json:"title"`
		ReleaseDate string  `json:"release_date"`
		VoteAverage float64 `json:"vote_average"`
	} `json:"results"`
}

func main() {
	q := url.Values{
		"with_genres":    {"878"},
		"sort_by":        {"vote_average.desc"},
		"vote_count.gte": {"1000"},
		"language":       {"en-US"},
	}
	req, _ := http.NewRequest("GET", "https://api.amodb.dev/3/discover/movie?"+q.Encode(), nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	client := &http.Client{Timeout: 10 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(res.Status)
	}
	var p Page
	if err := json.NewDecoder(res.Body).Decode(&p); err != nil {
		panic(err)
	}
	fmt.Println("pages:", p.TotalPages)
	for _, m := range p.Results {
		fmt.Println(m.VoteAverage, m.Title, m.ReleaseDate)
	}
}
```

### Parameters

| Parameter                                              | In    | Required   | Description                                                                                                                                                                                                                                                                                                                                |
| ------------------------------------------------------ | ----- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `api_key`                                              | query | one of two | Your key. Use this or the `Authorization: Bearer` header.                                                                                                                                                                                                                                                                                  |
| `sort_by`                                              | query | no         | Field and direction such as `vote_average.desc`. Fields: `popularity`, `vote_average`, `vote_count`, `primary_release_date`, `revenue`, `title`, `original_title`, each with `.asc` or `.desc`. `title` and `original_title` both sort by the original title. An unknown value falls back to `popularity.desc`, which is also the default. |
| `with_genres`                                          | query | no         | Genre ids. A comma means AND, a pipe character means OR.                                                                                                                                                                                                                                                                                   |
| `without_genres`                                       | query | no         | Genre ids to exclude.                                                                                                                                                                                                                                                                                                                      |
| `with_cast`, `with_crew`                               | query | no         | Person ids. Find them with [search](https://amodb.dev/search/).                                                                                                                                                                                                                                                                            |
| `with_people`                                          | query | no         | Person ids that appear as cast or crew.                                                                                                                                                                                                                                                                                                    |
| `with_companies`, `without_companies`                  | query | no         | Production company ids to require or exclude. Comma and pipe work as in `with_genres`.                                                                                                                                                                                                                                                     |
| `with_keywords`, `without_keywords`                    | query | no         | Keyword ids to require or exclude. See [keywords](https://amodb.dev/keywords/).                                                                                                                                                                                                                                                            |
| `with_origin_country`                                  | query | no         | Uppercase 2 letter production country code such as `FR`.                                                                                                                                                                                                                                                                                   |
| `primary_release_year`                                 | query | no         | Four digit year of the primary release date.                                                                                                                                                                                                                                                                                               |
| `year`                                                 | query | no         | Four digit year. Matches any release date in that year, so it returns more titles than `primary_release_year`.                                                                                                                                                                                                                             |
| `primary_release_date.gte`, `primary_release_date.lte` | query | no         | Lower and upper bound for the primary release date, `YYYY-MM-DD`.                                                                                                                                                                                                                                                                          |
| `release_date.gte`, `release_date.lte`                 | query | no         | Lower and upper bound for any release date in any country, `YYYY-MM-DD`.                                                                                                                                                                                                                                                                   |
| `region`                                               | query | no         | Uppercase 2 letter country code. Replaces `release_date` in each result with the date in that country. It does not remove titles.                                                                                                                                                                                                          |
| `certification_country`, `certification`               | query | no         | Age rating filter, for example `US` and `PG`. `certification` is ignored without a country. `certification.gte` and `certification.lte` give a range.                                                                                                                                                                                      |
| `vote_average.gte`, `vote_average.lte`                 | query | no         | Minimum and maximum average score, 0 to 10.                                                                                                                                                                                                                                                                                                |
| `vote_count.gte`, `vote_count.lte`                     | query | no         | Minimum and maximum number of votes.                                                                                                                                                                                                                                                                                                       |
| `with_runtime.gte`, `with_runtime.lte`                 | query | no         | Minimum and maximum runtime in minutes.                                                                                                                                                                                                                                                                                                    |
| `with_original_language`                               | query | no         | Original language as a 2 letter code such as `fr` or `ko`.                                                                                                                                                                                                                                                                                 |
| `with_watch_providers`                                 | query | no         | Provider ids. Needs `watch_region`, and is ignored without it. Ids come from [genres and lookup lists](https://amodb.dev/genres/).                                                                                                                                                                                                         |
| `watch_region`                                         | query | no         | Uppercase 2 letter country code for provider availability.                                                                                                                                                                                                                                                                                 |
| `include_adult`                                        | query | no         | Accepted and ignored. Adult titles are never returned.                                                                                                                                                                                                                                                                                     |
| `language`                                             | query | no         | TMDB tag such as `en-US` or `pl-PL`. 14 base languages are served. Default `en-US`.                                                                                                                                                                                                                                                        |
| `page`                                                 | query | no         | Page number from 1 to 500. Default 1.                                                                                                                                                                                                                                                                                                      |

### Response fields

| Field                                      | Type              | Notes                                                                                                                       |
| ------------------------------------------ | ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `page`, `total_pages`, `total_results`     | integer           | Paging for this filter set. Broad filters report at most 1001 pages and 20001 results, but only pages 1 to 500 can be read. |
| `results`                                  | array             | Up to 20 movies. Empty when no title matches every filter.                                                                  |
| `id`                                       | integer           | Movie id for [movie details](https://amodb.dev/movies/).                                                                    |
| `title`, `original_title`, `overview`      | string            | Localized text and the original title.                                                                                      |
| `original_language`                        | string            | Two letter code of the language the movie was made in.                                                                      |
| `release_date`                             | string            | `YYYY-MM-DD`.                                                                                                               |
| `genre_ids`                                | array of integers | Ids only. Map them to names with `/genre/movie/list`.                                                                       |
| `poster_path`, `backdrop_path`             | string or null    | Append to `https://img.amodb.dev/t/p/{size}`.                                                                               |
| `vote_average`, `vote_count`, `popularity` | number            | Score and ranking values.                                                                                                   |
| `adult`, `video`                           | boolean           | `adult` is always `false`.                                                                                                  |

### Example response

```json
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/wLWvHcQz7N13DvSOTV7bHgwGXpT.jpg",
      "genre_ids": [878, 12],
      "id": 687163,
      "original_language": "en",
      "original_title": "Project Hail Mary",
      "overview": "Science teacher Ryland Grace wakes up on a spaceship light years from home with no recollection of who he i...",
      "popularity": 101.0341,
      "poster_path": "/yihdXomYb5kTeSivtFndMy5iDmf.jpg",
      "release_date": "2026-03-15",
      "title": "Project Hail Mary",
      "video": false,
      "vote_average": 8.635,
      "vote_count": 8354
    },
    {
      "adult": false,
      "backdrop_path": "/Ys8UIGWJpd2TMuQk8fU77W3mBz.jpg",
      "genre_ids": [12, 18, 878],
      "id": 157336,
      "original_language": "en",
      "original_title": "Interstellar",
      "overview": "The adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitatio...",
      "popularity": 74.3949,
      "poster_path": "/yQvGrMoipbRoddT0ZR8tPoR7NfX.jpg",
      "release_date": "2014-11-04",
      "title": "Interstellar",
      "video": false,
      "vote_average": 8.491,
      "vote_count": 41526
    },
    {
      "adult": false,
      "backdrop_path": "/tanwf7KOwQ6dxZzehKsXkffIJOd.jpg",
      "genre_ids": [878, 28, 12],
      "id": 969681,
      "original_language": "en",
      "original_title": "Spider-Man: Brand New Day",
      "overview": "Fighting crime full-time as Spider-Man in a world that doesn't remember him...",
      "popularity": 663.5004,
      "poster_path": "/bjiS5ipwxb9JFy3XRRN4OAilSeX.jpg",
      "release_date": "2026-07-29",
      "title": "Spider-Man: Brand New Day",
      "video": false,
      "vote_average": 8.468,
      "vote_count": 5684
    }
  ],
  "total_pages": 39,
  "total_results": 772
}
```

The reply was fetched from the live API for the example request. It is cut to 3 of the 20 entries on page 1, and long `overview` strings are shortened.

## Combine filters

Separate parameters combine with AND. Inside one parameter, a comma means AND and a pipe character means OR. In a raw URL the pipe is written `%7C`, and clients that encode query values do this for you.

| Goal                                | Request                                                                                   |
| ----------------------------------- | ----------------------------------------------------------------------------------------- |
| Best rated sci-fi with enough votes | `/discover/movie?with_genres=878&sort_by=vote_average.desc&vote_count.gte=1000`           |
| Action or adventure                 | `/discover/movie?with_genres=28%7C12`                                                     |
| Action but not animation            | `/discover/movie?with_genres=28&without_genres=16`                                        |
| One actor with one director         | `/discover/movie?with_cast=6193&with_crew=525`                                            |
| Long films from one year            | `/discover/movie?primary_release_year=2010&with_runtime.gte=150`                          |
| Releases in a date window           | `/discover/movie?primary_release_date.gte=2026-10-01&primary_release_date.lte=2026-10-31` |
| Films in one original language      | `/discover/movie?with_original_language=fr`                                               |
| Streaming in one country            | `/discover/movie?with_watch_providers=8&watch_region=US`                                  |
| Rated R in the US                   | `/discover/movie?certification_country=US&certification=R`                                |
| Without a keyword                   | `/discover/movie?with_keywords=818&without_keywords=4565`                                 |

`with_original_language` filters by the language a title was made in, and it accepts codes outside the 14 served languages, for example `ja` or `ko`. The `language` parameter still decides the language of the text you get back. In the provider row, `8` is Netflix. Read provider ids from `/watch/providers/movie`.

## Discover TV shows

`GET /3/discover/tv` uses the same envelope and paging. Entries hold `name`, `original_name`, `first_air_date`, `origin_country` and `genre_ids`, and genre ids come from `/genre/tv/list`, which holds the merged TV ids such as `10765` for Sci-Fi and Fantasy.

| Parameter                                                                                              | Description                                                                                                                                                                      |
| ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `first_air_date.gte`, `first_air_date.lte`                                                             | Lower and upper bound for the first air date, `YYYY-MM-DD`.                                                                                                                      |
| `first_air_date_year`                                                                                  | Four digit year of the first air date.                                                                                                                                           |
| `air_date.gte`, `air_date.lte`                                                                         | Lower and upper bound for the dates a show has episodes airing, `YYYY-MM-DD`.                                                                                                    |
| `with_networks`                                                                                        | Network ids. Find them on [companies and networks](https://amodb.dev/companies-networks/).                                                                                       |
| `with_status`                                                                                          | Numeric status code: `0` returning series, `2` in production, `3` ended, `4` canceled.                                                                                           |
| `with_type`                                                                                            | Numeric show type: `0` documentary, `1` news, `2` miniseries, `3` reality, `4` scripted, `5` talk show, `6` video.                                                               |
| `sort_by`                                                                                              | `popularity`, `vote_average`, `vote_count`, `first_air_date`, `name` or `original_name`, each with `.asc` or `.desc`. `name` and `original_name` both sort by the original name. |
| `with_genres`, `without_genres`, `with_companies`, `with_keywords`, `without_keywords`                 | Same rules as for movies.                                                                                                                                                        |
| `vote_average.gte`, `vote_count.gte`, `with_runtime.gte`, `with_runtime.lte`, `with_original_language` | Same rules as for movies.                                                                                                                                                        |
| `with_watch_providers`, `watch_region`, `language`, `page`                                             | Same rules as for movies.                                                                                                                                                        |

A request such as `/discover/tv?first_air_date.gte=2025-01-01&with_genres=18&vote_count.gte=100&sort_by=popularity.desc` lists drama series that started in 2025 or later. Read a result in detail on [TV series](https://amodb.dev/tv-series/).

## Errors

| HTTP | status_code | When                                                               |
| ---- | ----------- | ------------------------------------------------------------------ |
| 401  | 7           | The key is missing or wrong.                                       |
| 402  | 36          | The plan period has ended.                                         |
| 405  | 37          | You called a write method.                                         |
| 429  | 25          | Over your requests per minute. Wait for the `Retry-After` seconds. |
| 500  | 11          | A `page` outside 1 to 500.                                         |
| 504  | 24          | The backend did not answer in time. Retry once.                    |

A filter set that matches nothing is not an error. You get HTTP 200 with an empty `results` array and `total_results` of 0. See [errors](https://amodb.dev/errors/) and [rate limits](https://amodb.dev/rate-limits/).

## Practical notes

- The edge caches each distinct filter set for 30 minutes and shares it across keys. The cache key uses the sorted query, so the order of your parameters does not matter.
- A page holds 20 entries and the last page is 500, so one filter set reaches 10,000 entries. Slice by date windows to go deeper. A broad filter reports `total_results` of 20001, which is a cap and not a real count.
- Always pair `sort_by=vote_average.desc` with `vote_count.gte`. Without it, titles with a few votes crowd the top.
- Ids in filters are numeric. Get person ids from [search](https://amodb.dev/search/), and genre and provider ids from [genres and lookup lists](https://amodb.dev/genres/).
- For ready-made lists such as popular or now playing, see [trending and lists](https://amodb.dev/trending-lists/).

## Questions and answers

### How deep can I page through discover results?

The page parameter runs from 1 to 500 with 20 entries per page, so one filter set reaches at most 10,000 entries. To read more, split the query into date windows with primary_release_date.gte and .lte.

### How do I combine genres with AND and OR?

Inside one parameter, a comma means AND and a pipe character means OR. The pipe is written %7C in a raw URL. Separate parameters always combine with AND.

### Why does sorting by rating return obscure titles?

A title with a handful of perfect votes tops the list. Add vote_count.gte, for example 1000, so only titles with enough votes are ranked by vote_average.

### How fresh are discover results?

The edge caches each distinct filter set for 30 minutes and shares it across keys. Every call still counts against your requests per minute, because the rate limit runs before the cache.

## Related pages

- [Search API](https://amodb.dev/search/): Search API with the same paths and JSON as TMDB v3. Find movies, TV shows, people, collections, companies and keywords by text, with year filters and paging.
- [Genres and lookup lists](https://amodb.dev/genres/): Genre list API with TMDB v3 paths: movie and TV genre ids with translated names, plus countries, languages, jobs, certifications and watch provider lists.
- [Trending and list endpoints](https://amodb.dev/trending-lists/): Trending movies API with TMDB v3 paths: day and week charts, plus now playing, popular, top rated and upcoming lists for movies and TV, cached for 30 minutes.
- [Keywords API](https://amodb.dev/keywords/): Movie keywords API with TMDB v3 paths: list plot tags for a movie or series, look up a keyword, list its movies and combine ids with discover with_keywords.
- [Companies and networks](https://amodb.dev/companies-networks/): Look up production companies and TV networks with TMDB v3 paths. Read name, country and logo by id, then filter discover by company or network id.
- [Rate limits](https://amodb.dev/rate-limits/): amodb limits each key by requests per minute in a sliding 60 second window: 20, 40 or 120 by plan. Read the rate limit headers and handle 429 with Retry-After.

Updated: 2026-10-18. Source: https://amodb.dev/discover/
