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

# YouTube Search

> Sell youtube.search on AnyAPI: the request your endpoint receives, the response it must return, and what the live trial grades.

Search YouTube and get matching videos (title, channel, views, length, publish time) as normalized JSON.

This page is the seller-facing summary of one catalog API, `youtube.search`. The transport rules that apply to every API a seller serves - signed requests, the response envelope, timeouts, and body limits - are on [the operator endpoint contract](https://getanyapi.com/docs/sell/operator-endpoint-contract.md).

## The price you compete with

AnyAPI publishes this API from **\$0.50 per 1,000 requests**. That is the price a buyer sees today, so it is the price your endpoint has to come in under to win traffic.

## The request your endpoint receives

| Field | Type | Required | What it means |
| - | - | - | - |
| `cursor` | string | no | Continuation token from a previous response for pagination. |
| `query` | string | yes | The YouTube search query. |
| `requireCursor` | boolean | no | Deprecated; send `requireFields: ["nextCursor"]` instead, which does exactly the same thing. Set true if you intend to page through results, so the request is only served by a source that can return a nextCursor. Omit it and routing is unchanged, with the cheapest source serving. This can raise your price: when the cheapest source cannot page, a source that can serves, and you are quoted and charged its price. It stays accepted so callers that already send it keep working. |
| `requireFields` | array | no | Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `nextCursor`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a result that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted. |
| `sortBy` | string | no | Sort order: "relevance" (default) or "popular" (most-viewed). One of: `relevance`, `popular`. Default `"relevance"`. |
| `uploadDate` | string | no | Filter by upload recency. Omit for any time. One of: `today`, `this_week`, `this_month`, `this_year`. |

> **Full input JSON Schema**
>
> ```json theme={"dark"}
> {
>   "$schema": "https://json-schema.org/draft/2020-12/schema",
>   "$id": "https://getanyapi.com/schemas/youtube.search.input.json",
>   "example": {
>     "query": "how to cook rice"
>   },
>   "title": "YouTube search input",
>   "type": "object",
>   "additionalProperties": false,
>   "required": [
>     "query"
>   ],
>   "properties": {
>     "query": {
>       "type": "string",
>       "description": "The YouTube search query."
>     },
>     "uploadDate": {
>       "type": "string",
>       "enum": [
>         "today",
>         "this_week",
>         "this_month",
>         "this_year"
>       ],
>       "description": "Filter by upload recency. Omit for any time."
>     },
>     "sortBy": {
>       "type": "string",
>       "enum": [
>         "relevance",
>         "popular"
>       ],
>       "default": "relevance",
>       "description": "Sort order: \"relevance\" (default) or \"popular\" (most-viewed)."
>     },
>     "cursor": {
>       "type": "string",
>       "description": "Continuation token from a previous response for pagination."
>     },
>     "requireCursor": {
>       "type": "boolean",
>       "deprecated": true,
>       "description": "Deprecated; send `requireFields: [\"nextCursor\"]` instead, which does exactly the same thing. Set true if you intend to page through results, so the request is only served by a source that can return a nextCursor. Omit it and routing is unchanged, with the cheapest source serving. This can raise your price: when the cheapest source cannot page, a source that can serves, and you are quoted and charged its price. It stays accepted so callers that already send it keep working."
>     },
>     "requireFields": {
>       "type": "array",
>       "uniqueItems": true,
>       "items": {
>         "type": "string",
>         "enum": [
>           "nextCursor"
>         ]
>       },
>       "description": "Optional; omit it and routing is unchanged, with the cheapest source serving. Name the output fields this request must be able to return, for example `nextCursor`, and it is served only by a source that returns every one of them. Fields you do not name are still returned whenever the serving source has them. This can raise your price: when the cheapest source cannot return a named field, a dearer source serves, and you are quoted and charged its price. A named field can still be absent on a result that genuinely lacks it. Naming a combination that no single source returns together is refused as invalid input, with no charge. On a paginated walk it applies to the first page only; later pages stay with the source that page chose, at the price it was quoted."
>     }
>   }
> }
> ```

## The response your endpoint must return

| Field | Type | Always present | What it means |
| - | - | - | - |
| `data` | null or object | yes | |
| `data.nextCursor` | string or null | no | Opaque cursor for the next page of results, or null when this lane has no more. Pass it back as cursor to continue. |
| `data.videos` | array | yes | |
| `data.videos[].authorAvatarUrl` | string | no | URL of the publishing channel's avatar image. |
| `data.videos[].authorId` | string | no | YouTube channel ID of the publishing channel. |
| `data.videos[].channel` | string | yes | |
| `data.videos[].createdUtc` | number | no | UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. |
| `data.videos[].id` | string | yes | |
| `data.videos[].image` | string | no | URL of the video thumbnail image, tracking query parameters stripped. |
| `data.videos[].lengthText` | string | yes | |
| `data.videos[].publishedTime` | string | yes | |
| `data.videos[].title` | string | yes | |
| `data.videos[].url` | string | yes | |
| `data.videos[].views` | integer | yes | |
| `found` | boolean | yes | |

Extra fields are kept, missing declared fields fail.

> **Full output JSON Schema**
>
> ```json theme={"dark"}
> {
>   "$schema": "https://json-schema.org/draft/2020-12/schema",
>   "$id": "https://getanyapi.com/schemas/youtube.search.output.json",
>   "title": "YouTube search output",
>   "type": "object",
>   "additionalProperties": false,
>   "required": [
>     "found",
>     "data"
>   ],
>   "properties": {
>     "found": {
>       "type": "boolean"
>     },
>     "data": {
>       "oneOf": [
>         {
>           "type": "null"
>         },
>         {
>           "type": "object",
>           "additionalProperties": false,
>           "required": [
>             "videos"
>           ],
>           "properties": {
>             "nextCursor": {
>               "type": [
>                 "string",
>                 "null"
>               ],
>               "description": "Opaque cursor for the next page of results, or null when this lane has no more. Pass it back as cursor to continue."
>             },
>             "videos": {
>               "x-anyapi-must-populate": true,
>               "type": "array",
>               "items": {
>                 "type": "object",
>                 "required": [
>                   "id",
>                   "url",
>                   "title",
>                   "channel",
>                   "views",
>                   "lengthText",
>                   "publishedTime"
>                 ],
>                 "properties": {
>                   "id": {
>                     "x-anyapi-must-populate": true,
>                     "type": "string"
>                   },
>                   "url": {
>                     "x-anyapi-must-populate": true,
>                     "type": "string"
>                   },
>                   "title": {
>                     "x-anyapi-must-populate": true,
>                     "type": "string"
>                   },
>                   "channel": {
>                     "x-anyapi-must-populate": true,
>                     "type": "string"
>                   },
>                   "views": {
>                     "type": "integer"
>                   },
>                   "lengthText": {
>                     "type": "string"
>                   },
>                   "publishedTime": {
>                     "x-anyapi-must-populate": true,
>                     "type": "string"
>                   },
>                   "image": {
>                     "type": "string",
>                     "description": "URL of the video thumbnail image, tracking query parameters stripped."
>                   },
>                   "createdUtc": {
>                     "type": "number",
>                     "description": "UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds."
>                   },
>                   "authorId": {
>                     "type": "string",
>                     "description": "YouTube channel ID of the publishing channel."
>                   },
>                   "authorAvatarUrl": {
>                     "type": "string",
>                     "description": "URL of the publishing channel's avatar image."
>                   }
>                 }
>               }
>             }
>           }
>         }
>       ]
>     }
>   }
> }
> ```

## Fields that must never come back empty

* `channel`
* `id`
* `publishedTime`
* `title`
* `url`
* `videos`

The repository marks these fields as ones every source must be able to fill. An empty value is only correct when the entity itself genuinely has nothing there.

## Not found, and how it differs from empty

When the entity does not exist, answer 2xx with the not-found shape:

```json theme={"dark"}
{
  "found": false,
  "data": null
}
```

An empty list is not a not-found: when the entity exists and has nothing to return, answer 2xx with `found: true` and an empty `videos` array.

This output schema declares no `reason` field, so `found: false` is the whole not-found answer.

## Example request

```json theme={"dark"}
{
  "query": "how to cook rice"
}
```

The compatibility checks fire the schema's example at your endpoint. After that, your answers are graded against what the currently serving source returned for the same real customer inputs.

## Next

* [The operator endpoint contract](https://getanyapi.com/docs/sell/operator-endpoint-contract.md) for signing, budgets, and the live trial.
* [Every API open to sellers](https://getanyapi.com/docs/sell/apis/index.md).
* Want to sell an API that is not on that list? Email [support@getanyapi.com](mailto:support@getanyapi.com).


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