One URL change. Same integration. This guide is the primary entry point for clients migrating an existing Tenor integration to GIPHY's Tenor-compatible endpoints with minimal code changes.
In most cases you can be up and running by creating a GIPHY API key, swapping your API host from tenor.googleapis.com to api.giphy.com, and confirming your product follows GIPHY branding requirements.
tenor.googleapis.com to api.giphy.comThe sections below document supported endpoints, parameters, and a few compatibility differences.
This guide helps you migrate an existing Tenor client to GIPHY’s Tenor-compatible endpoints with minimal code changes. It is intended for clients that already know the Tenor API shape and want to send Tenor-style requests to GIPHY with minimal changes.
Some response fields depend on API key capabilities.
| Field | Endpoints | When Returned |
|---|---|---|
tags | /v2/search, /v2/featured, /v2/posts | Only when the API key has tags access enabled |
If a guarded field is not available for your API key, it is omitted from the response.
searchfilter=sticker,static and searchfilter=sticker,-static are not supported. Use searchfilter=sticker only.
client_key value across all API calls for a given integration.Pagination is supported on /v2/search and /v2/featured.
next.next value back as pos.pos as opaque./v2/posts does not return next.curl "https://api.giphy.com/v2/search?q=cat&key=$API_KEY&limit=20"curl "https://api.giphy.com/v2/search?q=cat&key=$API_KEY&limit=20&pos=$NEXT"contentfilter maps to the Motion Picture Association rating system:
off - G, PG, PG-13, Rlow - G, PG, PG-13medium - G, PGhigh - GRefer to GIPHY rating guide for more details
media_filter supports the following rendition names:
| Rendition Name | Notes |
|---|---|
preview | Single-frame GIF used for preloading. |
gif | Full GIF that loops automatically. |
mediumgif | Reduced GIF, typically 2-5 MB. |
tinygif | Small GIF, typically around 200px. |
nanogif | Very small GIF, typically around 100px. |
mp4 | Full MP4 that plays once. |
loopedmp4 | Looping MP4 with a 15s loop. |
tinymp4 | Reduced MP4 optimized for mobile. |
nanomp4 | Smallest MP4 for low bandwidth use. |
webm | Supported by the compatibility layer, but not a video format in the native guide. |
webp_transparent | Full quality WebP with transparency. |
tinywebp_transparent | Around 200px with transparency. |
nanowebp_transparent | Around 100px with transparency. |
gif_transparent | GIF with transparency layer. |
tinygif_transparent | Small transparent GIF. |
nanogif_transparent | Smallest transparent GIF. |
tinywebm | Not currently supported. |
nanowebm | Not currently supported. |
/v2/registershare is not implemented as a direct compatibility endpoint. For share analytics, use the analytics URLs returned on each GIPHY item instead.
/v2/registershare is analytics.onsent.url.analytics.onclick.url for click events.analytics.onload.url for load events.Fire these URLs client-side as GET requests and append random_id and ts for tracking.
const gif = data.results[0];const timestamp = Date.now();
// Share/send event equivalent for /v2/registersharefetch(`${gif.analytics.onsent.url}?random_id=${random_id}&ts=${timestamp}`);
// Additional supported analytics eventsfetch(`${gif.analytics.onclick.url}?random_id=${random_id}&ts=${timestamp}`);fetch(`${gif.analytics.onload.url}?random_id=${random_id}&ts=${timestamp}`);❗Parameters not listed in endpoint details are ignored.
Search for GIFs or stickers while keeping the Tenor-compatible request and response shape.
results and optional next.searchfilter=sticker for sticker content.searchfilter=static, searchfilter=-static, ar_range, and random are not supported.| Gif URL | Sticker URL |
|---|---|
| api.giphy.com/v2/search | api.giphy.com/v2/search?searchfilter=sticker |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| q: string | Yes | cat | Search query. |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
| searchfilter: string | No | sticker | Partial support. sticker is supported. Default behavior returns GIF content. |
| country: string | No | US | Recommended country code. Default: US. |
| locale: string | No | en_US | Recommended locale code. Default: en_US. |
| contentfilter: string | No | medium | Supported values: off, low, medium, high. Default: off. |
| media_filter: string | No | tinygif,mp4 | Partial support for mapped rendition names only. |
| limit: integer | No | 10 | Result count limit. Default: 20. Maximum: 50. |
| pos: string | No | $NEXT | Pagination cursor. Send the previous response next value back as pos. |
| ar_range: string | No | all | Ignored |
| random: boolean | No | true | Ignored |
curl "https://api.giphy.com/v2/search?q=cat&key=$API_KEY&client_key=my_app&limit=10&country=US&locale=en_US"curl "https://api.giphy.com/v2/search?q=cat&key=$API_KEY&client_key=my_app&searchfilter=sticker&limit=10"curl "https://api.giphy.com/v2/search?q=cat&key=$API_KEY&media_filter=gif,mp4,tinygif"Get featured or trending GIFs or stickers through the compatibility layer.
results and optional next.searchfilter=sticker for sticker content.searchfilter=static, searchfilter=-static, and ar_range are not supported.| Gif URL | Sticker URL |
|---|---|
| api.giphy.com/v2/featured | api.giphy.com/v2/featured?searchfilter=sticker |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
| searchfilter: string | No | sticker | Partial support. sticker is supported. |
| country: string | No | US | Recommended country code. Default: US. |
| locale: string | No | en_US | Recommended locale code. Default: en_US. |
| contentfilter: string | No | medium | Supported values: off, low, medium, high. Default: off. |
| media_filter: string | No | tinygif,mp4 | Partial support for mapped rendition names only. |
| limit: integer | No | 10 | Result count limit. Default: 20. Maximum: 50. |
| pos: string | No | $NEXT | Pagination cursor. Send the previous response next value back as pos. |
| ar_range: string | No | all | Ignored |
curl "https://api.giphy.com/v2/featured?key=$API_KEY&client_key=my_app&limit=10&country=US&locale=en_US"curl "https://api.giphy.com/v2/featured?key=$API_KEY&searchfilter=sticker&limit=10"Fetch content by ID while preserving the Tenor-style response contract.
results and do not include next.contentfilter on this route.| URL |
|---|
| api.giphy.com/v2/posts |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| ids: string | Yes | id1,id2,id3 | Comma-separated list of content IDs. Max 50 ids |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
| media_filter: string | No | gif,mp4 | Partial support for mapped rendition names only. |
curl "https://api.giphy.com/v2/posts?key=$API_KEY&client_key=my_app&ids=id1,id2,id3"curl "https://api.giphy.com/v2/posts?key=$API_KEY&ids=id1,id2,id3&media_filter=gif,mp4"Fetch category metadata in a Tenor-style format.
tags.featured and trending values are supported for type.| URL |
|---|
| api.giphy.com/v2/categories |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| client_key: string | No | my_app | Preserved in generated category search URLs. |
| type: string | No | featured | Partial support. featured and trending are supported. |
| contentfilter: string | No | medium | Affects image visibility and generated URLs. Default: off. |
curl "https://api.giphy.com/v2/categories?key=$API_KEY&client_key=my_app&country=US&locale=en_US&type=featured&contentfilter=medium"Get search autocomplete suggestions. Returns up to 20 terms.
results.q returns empty results.| URL |
|---|
| api.giphy.com/v2/autocomplete |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| q: string | Yes | exc | Autocomplete query term. |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
| limit: integer | No | 10 | Suggestion count limit. Default: 20. Maximum: 50. |
curl "https://api.giphy.com/v2/autocomplete?key=$API_KEY&client_key=my_app&q=exc&limit=10&country=US&locale=en_US"Get related search terms. Returns up to 20 terms.
results.q returns empty results.| URL |
|---|
| api.giphy.com/v2/search_suggestions |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| q: string | Yes | smile | Related-term query term. |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
curl "https://api.giphy.com/v2/search_suggestions?key=$API_KEY&client_key=my_app&q=smile&limit=10&country=US&locale=en_US"Get trending search terms. Returns up to 20 terms.
results.limit is not currently implemented on this compatibility route.| URL |
|---|
| api.giphy.com/v2/trending_terms |
| Request Parameters: | Required: | Example: | Description: |
|---|---|---|---|
| key: string | Yes | $API_KEY | GIPHY API key. |
| client_key: string | No | my_app | Recommended client identifier for your integration. |
curl "https://api.giphy.com/v2/trending_terms?key=$API_KEY&client_key=my_app&country=US&locale=en_US"The compatibility guide above is the fastest path for existing Tenor clients. Use the following migration guide to understand and implement a native GIPHY experience with direct /v1/* endpoints, native schema mapping, and deeper integration details.
Use this section when you want to move beyond the compatibility layer and adopt native GIPHY endpoints and response models.
The compatibility documentation above should take precedence for minimal-change migrations from Tenor.
This guide provides comprehensive information for developers migrating from the Tenor API to the GIPHY API. While both APIs provide access to GIF and sticker content, there are important differences in endpoints, parameters, response structures, and features.
Want a faster, easier integration? Check out our official GIPHY SDKs for JavaScript, iOS, and Android. The SDK handles pagination, analytics tracking, and rendition optimization automatically, letting you focus on building great experiences instead of managing API details.
API keys from Tenor will not work with GIPHY.
You must create a new API key in the GIPHY Developer Dashboard.
All API Keys start as beta keys, which are rate limited (100 searches/API calls per hour). If you need more than 100 API calls per hour you will need to submit an application to upgrade your API Key to production status via your dashboard. We will review your application and if you qualify, a member of our team will reach out via email to discuss pricing.
Plan for testing time as pagination and analytics tracking work differently.
Authentication differs between Tenor and GIPHY:
api_key query parameter instead of Tenor's key parametercurl "https://tenor.googleapis.com/v2/search?q=excited&key=YOUR_TENOR_KEY"curl "https://api.giphy.com/v1/gifs/search?q=excited&api_key=YOUR_GIPHY_KEY"| Platform | Base URL |
|---|---|
| Tenor | https://tenor.googleapis.com |
| GIPHY | https://api.giphy.com |
Below is a comprehensive mapping of Tenor endpoints to their GIPHY equivalents:
| Tenor | GIPHY | Notes |
|---|---|---|
/v2/search | /v1/gifs/search | See pagination differences below |
| Tenor | GIPHY | Notes |
|---|---|---|
/v2/search?searchfilter=sticker | /v1/stickers/search | Uses URL slug instead of query parameter |
remove_low_contrast=true| Tenor | GIPHY |
|---|---|
/v2/featured | /v1/gifs/trending |
/v2/featured?searchfilter=sticker | /v1/stickers/trending |
| Tenor | GIPHY | Notes |
|---|---|---|
/v2/categories | /v1/gifs/categories | GIPHY provides the category's featured GIF |
| Tenor | GIPHY |
|---|---|
/v2/autocomplete | /v1/gifs/search/tags |
Both require q parameter. Optional pagination parameters supported.
| Tenor | GIPHY |
|---|---|
/v2/search_suggestions (uses q) | /v1/tags/related/<term> |
| Tenor | GIPHY |
|---|---|
/v2/trending_terms | /v1/trending/searches |
| Tenor | GIPHY | Notes |
|---|---|---|
/v2/posts?ids=<ids> | /v1/gifs?ids=<ids> | Both use comma-separated list |
| — | /v1/gifs/:id | GIPHY also offers single-ID endpoint |
| Tenor | GIPHY |
|---|---|
/v2/search?random=true | /v1/gifs/random |
/v2/search?random=true&searchfilter=sticker | /v1/stickers/random |
Tenor returns a list of random results.
GIPHY returns a single random item.
| Tenor | GIPHY |
|---|---|
/v2/anonid | /v1/randomid |
Tenor uses a token-based pagination system with pos and next values.
GIPHY uses a simple numeric offset model with offset and limit.
Unlike Tenor, which uses opaque cursor tokens, GIPHY uses a simple numeric offset representing how many results to skip.
offset=25 returns results starting from the 26th itemlimit parameternext cursor// First pageconst response1 = await fetch('https://tenor.googleapis.com/v2/search?q=cats&key=KEY');const data1 = await response1.json();
// Next page using tokenconst response2 = await fetch(`https://tenor.googleapis.com/v2/search?q=cats&key=KEY&pos=${data1.next}`);// First pageconst response1 = await fetch('https://api.giphy.com/v1/gifs/search?q=cats&api_key=KEY&limit=25&offset=0');const data1 = await response1.json();
// Next page using numeric offsetconst response2 = await fetch('https://api.giphy.com/v1/gifs/search?q=cats&api_key=KEY&limit=25&offset=25');| Tenor Parameter | GIPHY Parameter | Values |
|---|---|---|
contentfilter | rating | Tenor: off | low | medium | high GIPHY: r | pg-13 | pg | g |
GIPHY defaults to r (restricted). Default and maximum rating can be configured for partners.
| Tenor | GIPHY | Notes |
|---|---|---|
anon_id | customer_id | Can be supplied in every GIPHY request |
| Tenor | GIPHY |
|---|---|
media_filter (basic | minimal) | bundle (clips_grid_picker | messaging_non_clips | sticker_layering | low_bandwidth)OR fields parameter for fields on demand |
Example: fields=images.original.url,slug
| Tenor | GIPHY |
|---|---|
locale (xx_YY or YY) | lang + country_code |
GIPHY allows proxying user requests with:
country_coderegion_codeThe following parameters work the same way in both APIs:
q - Search term(s)Tenor centralizes tracking through /v2/registershare endpoint.
GIPHY decentralizes tracking with per-item analytics URLs.
Tenor uses a dedicated endpoint /v2/registershare where your backend logs share events directly.
Each GIPHY item includes an analytics object with event-specific URLs:
analytics.onload.url - Fire when item loadsanalytics.onclick.url - Fire when user clicksanalytics.onsent.url - Fire when message is sentTo register interactions:
customer_id and ts (timestamp) parametersThe customer_id should be an identifier assigned to a user in your platform and kept consistent across that user's requests. If you do not have your own user ID, use the identifier returned by the Random ID Endpoint.
// Log share event to backendawait fetch('https://tenor.googleapis.com/v2/registershare', { method: 'POST', body: JSON.stringify({ id: gifId, key: API_KEY, q: searchTerm })});// Use your own stable user ID, or the value returned by GIPHY's Random ID endpointconst customerId = getUserIdFromYourPlatform(); // or (await fetch('https://api.giphy.com/v1/randomid?api_key=YOUR_API_KEY').then((res) => res.json())).random_id
// Use this customer_id for all API requests and analytics for this userconst searchResponse = await fetch(`https://api.giphy.com/v1/gifs/search?q=cats&api_key=YOUR_API_KEY&customer_id=${customerId}`);const data = await searchResponse.json();
// Fire analytics URLs client-sideconst gif = data.data[0];const timestamp = Date.now();
// When GIF loadsfetch(`${gif.analytics.onload.url}?customer_id=${customerId}&ts=${timestamp}`);
// When user clicksfetch(`${gif.analytics.onclick.url}?customer_id=${customerId}&ts=${timestamp}`);
// When message is sentfetch(`${gif.analytics.onsent.url}?customer_id=${customerId}&ts=${timestamp}`);| Platform | Response Structure |
|---|---|
| Tenor | • Uses standard HTTP status codes • Known errors return HTTP 200 with error field• Unknown errors use non-200 codes |
| GIPHY | • All responses include meta object• meta contains: status, msg, response_id• Errors indicated via meta.status• Some errors return empty data with 4xx status |
If meta.status=200, meta.response_id="" and body is empty, treat as an error.
Always validate data presence in GIPHY responses.
| Tenor | GIPHY |
|---|---|
results, next | data, pagination, meta |
Tenor uses next token; GIPHY uses offset + limit in the pagination object.
{ "results": [...], "next": "CAgQABokCOWW3Y8GMgYIChCAgBASJAj6z96PBjIGCAoQgJAQEiQIqqDejwYyBggKEIDgHA"}{ "data": [...], "pagination": { "total_count": 50000, "count": 25, "offset": 0 }, "meta": { "status": 200, "msg": "OK", "response_id": "abc123def456" }}| Code | Tenor Description | GIPHY Description | Migration Notes |
|---|---|---|---|
| 200 | OK or known error | OK; check meta | Validate data presence |
| 3xx | Redirect (rare) | Not documented | Ignore or handle normally |
| 400 | Not documented | Bad request | Validate params |
| 401 | Not documented | Unauthorized | Verify API key |
| 403 | Not documented | Forbidden | Ensure permissions |
| 404 | Not found | Not found | Show user-friendly message |
| 414 | Not documented | URI too long | Trim queries |
| 429 | Rate limit exceeded | Too many requests | 100 requests/hour beta limit |
| 5xx | Server error | Synthetic 200 | Retry with backoff |
| Tenor Field | GIPHY Equivalent | Notes |
|---|---|---|
id | id | String ID |
title | title | — |
content_description | title / alt_text | If available |
media_formats | images | Remap renditions (see below) |
created | create_datetime / update_datetime / import_datetime / trending_datetime | Unix timestamp → ISO 8601 |
tags | tags / featured_tags / user_tags | Partner-only |
| Tenor | GIPHY | Notes |
|---|---|---|
content_rating | rating | — |
url | url | Also: embed_url, source_post_url |
itemurl | bitly_url | Shareable short URL |
hasaudio | — | No equivalent |
hascaption | — | No equivalent |
flags | is_sticker / is_low_contrast | Boolean flags |
GIPHY user data includes username and public user info when available.
Use MP4 and WEBP formats where supported to maximize quality and reduce load time.
GIPHY provides more rendition options than Tenor for better optimization.
| Tenor Format | Typical Use | GIPHY Equivalent | Notes |
|---|---|---|---|
preview | Single-frame GIF | preview_gif / fixed_*_still | Preloading |
gif | Full GIF | original | Loops automatically |
mediumgif | Reduced GIF | downsized / downsized_medium | 2–5 MB |
tinygif | Small GIF | fixed_width / fixed_height | ~200px |
nanogif | Very small GIF | fixed_*_small | ~100px |
mp4 | Full MP4 | original.mp4 / hd.mp4 / 4k.mp4 | Plays once |
loopedmp4 | Looping MP4 | looping.mp4 | 15s loop |
tinymp4 | Reduced MP4 | fixed_*.mp4 | Mobile optimized |
nanomp4 | Smallest MP4 | fixed_*_small.mp4 / preview.mp4 | Low bandwidth |
webm | Video | original.webp (animated) | Not a video format |
| Tenor Format | GIPHY Equivalent | Notes |
|---|---|---|
webp_transparent | original.webp | Full quality WebP with transparency |
tinywebp_transparent | fixed_width.webp / fixed_height.webp | ~200px with transparency |
nanowebp_transparent | fixed_*_small.webp | ~100px with transparency |
gif_transparent | original.gif (stickers) | GIF with transparency layer |
tinygif_transparent | fixed_width.gif / fixed_height.gif | Small transparent GIF |
nanogif_transparent | fixed_*_small.gif | Smallest transparent GIF |
// GIPHY response structureconst gif = data.data[0];
// Original (highest quality)const originalUrl = gif.images.original.url;const originalMp4 = gif.images.original.mp4;
// Fixed width (responsive)const fixedWidth = gif.images.fixed_width.url;const fixedWidthMp4 = gif.images.fixed_width.mp4;
// Downsized (optimized)const downsized = gif.images.downsized.url;
// Preview (still frame)const previewGif = gif.images.preview_gif.url;
// For stickers with transparencyconst stickerWebp = gif.images.original.webp;Follow this checklist to ensure a smooth migration from Tenor to GIPHY:
tenor.googleapis.com to api.giphy.comkey parameter with api_key in all requests/v2/search → /v1/gifs/search/v1/stickers/search instead of searchfilter param/v2/featured → /v1/gifs/trending/v2/autocomplete → /v1/gifs/search/tags/v2/search?random=true → /v1/gifs/random/v2/posts → /v1/gifspos token → offset numeric valuecontentfilter → ratinganon_id → customer_idlocale → lang + country_codemedia_filter → bundle or fieldsresults → datameta object validationnext token)media_formats → images/v2/registershare backend callsanalytics.onload.url when GIF loadsanalytics.onclick.url when user clicksanalytics.onsent.url when message sentcustomer_id (your own stable user ID or the value returned by the Random ID Endpoint) and ts to analytics URLs