Tenor → GIPHY-compatible layer

Migrate from Tenor to Heypster through our GIPHY-compatible API surface

The simplest way to move away from Tenor is to target the Heypster layer we built on the GIPHY model. In practice, you are not replacing Tenor with a Tenor-compatible API, but with a Heypster API exposed under `/giphy` using GIPHY conventions.

The main diff is concentrated around three topics: `key` becomes `api_key`, cursor pagination `pos/next` becomes numeric `offset/limit`, and parsing moves from `results/media_formats` to `data/images`.

key → api_key

Authentication aligned with GIPHY

pos → offset

Pagination to recalculate

results → data

New response schema

Before / after

// before (Tenor)
const API_HOST = 'https://tenor.googleapis.com';
fetch(`${API_HOST}/v2/search?q=party&key=YOUR_TENOR_KEY`);

// after (Heypster)
const API_HOST = 'https://heypster-gif.com/giphy';
fetch(`${API_HOST}/v1/gifs/search?q=party&api_key=YOUR_HEYPSTER_API_KEY`);

1. Authentication and API host

When coming from Tenor, the first change is replacing the host `https://tenor.googleapis.com` with `https://heypster-gif.com/giphy`. The second is replacing the `key` parameter with `api_key`, in line with the GIPHY-compatible layer exposed by Heypster.

Important: this page documents only the surface actually exposed by your GIPHY-based Heypster API. The examples below therefore align with the available `/giphy/v1/...` routes.
# Tenor
curl "https://tenor.googleapis.com/v2/search?q=excited&key=YOUR_TENOR_KEY"

# Heypster
curl "https://heypster-gif.com/giphy/v1/gifs/search?q=excited&api_key=YOUR_HEYPSTER_API_KEY"

2. Endpoint mapping

Tenor Heypster Notes
https://tenor.googleapis.com/v2/search https://heypster-gif.com/giphy/v1/gifs/search Primary keyword search.
https://tenor.googleapis.com/v2/featured https://heypster-gif.com/giphy/v1/gifs/trending Discovery / trending feed.
https://tenor.googleapis.com/v2/categories https://heypster-gif.com/giphy/v1/gifs/categories Categories exposed by the GIPHY-compatible layer.
https://tenor.googleapis.com/v2/autocomplete https://heypster-gif.com/giphy/v1/gifs/search/tags Query suggestions.
https://tenor.googleapis.com/v2/search_suggestions https://heypster-gif.com/giphy/v1/tags/related/{term} Suggestions related to a specific term.
https://tenor.googleapis.com/v2/trending_terms https://heypster-gif.com/giphy/v1/trending/searches Trending search terms.
https://tenor.googleapis.com/v2/search?random=true https://heypster-gif.com/giphy/v1/gifs/random Heypster returns a single item, not a random list.
https://tenor.googleapis.com/v2/posts?ids=<ids> https://heypster-gif.com/giphy/v1/gifs?ids=<ids> Comma-separated ID list.
https://tenor.googleapis.com/v2/anonid https://heypster-gif.com/giphy/v1/randomid Random identifier on the GIPHY-compatible API side.

3. Parameter changes

Tenor Heypster Description
key api_key The parameter name changes.
q q The search term stays the same.
pos offset Tenor uses a token; Heypster follows GIPHY with numeric pagination.
contentfilter rating Content filtering follows the GIPHY vocabulary.
anon_id / anonid /giphy/v1/randomid Use the `randomid` workflow if your product needs a consistent anonymous identifier.
media_formats images Format mapping goes through the `images` object.

5. Response parsing

A Tenor parser usually reads `results` and `next`. Here you need to read `data`, `pagination`, and `meta`. This is the main structural change after pagination.

{
  "data": [],
  "pagination": {
    "total_count": 500,
    "count": 25,
    "offset": 0
  },
  "meta": {
    "status": 200,
    "msg": "OK",
    "response_id": "heypster-response-id"
  }
}
const payload = await response.json();

const items = payload.data;
const offset = payload.pagination?.offset ?? 0;
const count = payload.pagination?.count ?? items.length;
const nextOffset = offset + count;
const status = payload.meta?.status;

6. Fields and renditions

Tenor Heypster Description
results[] data[] The main array changes name.
next pagination.offset + pagination.count No opaque cursor anymore: the next page is calculated.
results[].media_formats data[].images Asset variants are read from `images`.
results[].content_rating data[].rating Content rating is read from `rating`.
results[].title / content_description data[].title Start by reading `title` as the main label.
Tenor rendition Heypster rendition Usage
gif images.original.url Primary GIF version.
tinygif images.fixed_width.url or images.fixed_height.url Lightweight version for picker or grid.
mediumgif images.downsized.url Optimized version for standard display.
preview images.preview_gif.url Fast preview.

7. Tracking

If your Tenor integration used `/v2/registershare`, plan a dedicated adaptation: the Heypster surface documented here focuses on search, discovery, and identification endpoints (`randomid`), not on a Tenor-style share-tracking endpoint.

The right approach is to keep or rebuild that tracking in your own application layer while using `/giphy/v1/randomid` if you need a consistent random identifier.

8. Full JavaScript example

const API_HOST = 'https://heypster-gif.com/giphy';
const API_KEY = 'YOUR_HEYPSTER_API_KEY';

export async function searchTenorLike(query, page = 0, limit = 25) {
    const offset = page * limit;
    const url = new URL(`${API_HOST}/v1/gifs/search`);

    url.searchParams.set('api_key', API_KEY);
    url.searchParams.set('q', query);
    url.searchParams.set('limit', String(limit));
    url.searchParams.set('offset', String(offset));
    url.searchParams.set('rating', 'g');

    const response = await fetch(url.toString());
    const payload = await response.json();

    return {
        items: payload.data,
        pagination: payload.pagination,
        nextOffset: (payload.pagination?.offset ?? 0) + (payload.pagination?.count ?? 0),
        meta: payload.meta,
    };
}

9. Migration checklist

  • Replace `https://tenor.googleapis.com` with `https://heypster-gif.com/giphy`.
  • Replace `key` with `api_key` in every request.
  • Map `search`, `featured`, `categories`, `autocomplete`, `search_suggestions`, `trending_terms`, `posts`, and `anonid`.
  • Replace `pos` / `next` pagination with `limit` / `offset`.
  • Adapt parsing from `results` to `data` and from `media_formats` to `images`.
  • Explicitly verify the formats your interface actually consumes.
  • Handle tracking separately if you used Tenor `registershare`.