Open-source project
SunkwiBOT/twitch-drops-api avatar
SunkwiBOT/twitch-drops-api

twitch-drops-api: drops and rewards on four endpoints with a 60 second cache

A public API providing detailed information on Twitch drops and campaigns, including rewards, timelines, and game-specific details.

28 stars1 forksTypeScriptLicense varies

At a glance

What is it?
A read-only HTTP service that publishes Twitch Drops campaigns and reward campaigns, with the legacy endpoints returning bare arrays and the v2 endpoints adding freshness metadata in the body. The generating scripts run under bun, and the data behind all four endpoints is the same Twitch cache.
Who is it for?
This fits a bot, a site or a script that needs to know which Twitch campaigns are running right now and cannot afford to talk to Twitch directly. It does not fit anything that needs to claim a drop, watch progress or authenticate a user, because nothing here writes back and the documented requests carry no credentials.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • SunkwiBOT/twitch-drops-api28 ★ · +4
Star history
Repository2026-08-242026-10-21
SunkwiBOT/twitch-drops-api2428

4 Stars in the last 58 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Four GET endpoints, one cache, a 60 second refresh interval

The service is four read-only endpoints over one shared piece of state. `/drops` and `/rewards` are the legacy pair, `/v2/drops` and `/v2/rewards` are the newer pair, and all four are backed by the same Twitch data cache, which refreshes every 60 seconds. Nothing is queried per request, so the latency you see is not Twitch's latency, and the freshness of what you get is bounded by that interval rather than by the moment you asked. There is no documented query parameter for forcing a refresh, no documented filter by game, and no documented pagination, so a caller who wants every campaign fetches the full list and filters locally. The base host is `twitch-drops-api.sunkwi.com`, and the drops page at `https://twitch-drops-api.sunkwi.com/drops` is the documented entry point.

Freshness moved out of headers and into the body for v2

The two generations disagree about where cache age lives. On the legacy endpoints the payload is the array and the freshness travels in response headers:

http
X-Last-Updated-At: 2026-06-14T12:34:56.789Z
X-Refresh-Interval-Seconds: 60

On v2 the freshness is part of the JSON body instead:

json
{
  "lastUpdatedAt": "2026-06-14T12:34:56.789Z",
  "refreshIntervalSeconds": 60,
  "data": []
}

The wrapper type is `TwitchDropsApiV2ResponseType<T>`, where `lastUpdatedAt` is a string or null, `refreshIntervalSeconds` is a number, and `data` carries the payload. The null case on `lastUpdatedAt` is the one detail worth planning around, since a client that assumes a parseable timestamp there will fail on the response where the field is absent rather than empty.

The v2 data field has the same array shape as v1

Moving metadata into the body did not change the payload. The v1 endpoints return `TwitchDropCampaignType[]` and `TwitchRewardCampaignType[]` directly, as the top level of the response. The v2 endpoints wrap the identical arrays, and the documentation states that the `data` field has the same array shape as the matching legacy response. So a migration is a matter of where you read from rather than what you parse: `await response.json()` becomes the array on v1 and the wrapper object on v2, and everything downstream of that is identical. The type parameter is what carries the distinction, since `TwitchDropsApiV2ResponseType<TwitchDropCampaignType[]>` and `TwitchDropsApiV2ResponseType<TwitchRewardCampaignType[]>` are different types over the same shape, so a TypeScript client gets the campaign type checked against the endpoint it called.

The generating scripts run under bun, not npm

package.json is small and declares its own runner. The scripts block is two entries:

code
"fetch:drops": "bun run fetch-drops.ts"
"fetch:rewards": "bun run fetch-rewards.ts"

Both point at bun rather than at node or tsx, so the `.ts` sources at the repository root are executed by bun directly. The package sets `"type": "module"`, so those files are ES modules. There is no dependencies block and no devDependencies block at all, which tells you the fetch scripts rely on what bun ships with rather than on a lockfile full of HTTP clients. The package is named `twitch-drops-api` at version 1.0.0, and there are no GitHub releases, so the version in package.json is the only version statement anywhere. Keywords cover drops, campaigns and rewards in both singular and API forms, and the author field points at `twitter.com/sunkwii`.

drops.json and rewards.json sit next to the scripts that produce them

The repository root is short enough to describe exactly: `.github/`, `README.md`, `drops.json`, `fetch-drops.ts`, `fetch-rewards.ts`, `package.json`, `rewards.json` and `types.ts`. The two scripts and the two JSON files line up one to one, so the pipeline shape is visible from the file names alone, with a TypeScript fetcher producing a JSON artifact per campaign kind. `types.ts` is where the documented response types are kept, which is why the docs can be exact about field names rather than approximate. No server code, no framework, no Dockerfile and no deployment configuration appear at the root, so whatever serves `twitch-drops-api.sunkwi.com` is not described in this repository. Nothing in the documented pipeline explains how the JSON artifacts reach that host.

A campaign object reports per-reward account connection state

`TwitchDropCampaignType` starts with `endAt`, `gameBoxArtURL`, `gameDisplayName` and `gameId`, then carries a `rewards` array where the interesting fields sit. Each reward has an `id`, an `accountLinkURL`, a `description`, a `detailsURL`, its own `endAt`, an `eventBasedDrops` field typed as `any[]`, a nested `game` object, a `self` object holding `isAccountConnected`, and an `allow` object holding `isEnabled` plus an optional `channels` array with `id`, `name` and an optional `displayName`. Several fields also carry a `__typename` string, which is GraphQL residue from wherever the upstream data is collected. Because `eventBasedDrops` is untyped, a TypeScript client gets no help there and has to read the shape at runtime.

The named consumer is a Discord bot, not a Twitch integration

The project documents one real use: a free Discord bot called Twitch Alerts, listed on the Discord application directory and on top.gg. It notifies a Discord server when a Twitch streamer goes live, and it sends alerts about available Twitch drops for specific games or for all games. That framing sets expectations for the API. This is a notification feed, which is why it is read-only, unauthenticated in the documented examples, and refreshed on a timer rather than on a webhook. The repository itself has no releases and no license field the platform recognises, while package.json declares ISC, so the licensing answer and the metadata answer disagree and the file in the repository is the one to read.

No auth, no rate limit and no error body are documented

The documented failure handling in the examples is a status check and a thrown error:

ts
if (!response.ok) {
  throw new Error(`HTTP error: ${response.status}`);
}

That is the whole contract on the error side. No status codes are enumerated, no error body shape is given, and no retry or backoff guidance exists. Authentication is never mentioned: the examples are plain GET requests with no key, no bearer token and no custom header, which is consistent with a public read-only feed and also means there is nothing to leak, but the docs do not say so in as many words. Rate limits are not documented either, which matters because every caller shares one cache refreshed every 60 seconds. The `TwitchRewardCampaignType` shape is introduced in the documentation but its body is cut off there, so the rewards object is the one place a client is working from a partial type.

Editorial conclusion

This fits a bot, a site or a script that needs to know which Twitch campaigns are running right now and cannot afford to talk to Twitch directly. It does not fit anything that needs to claim a drop, watch progress or authenticate a user, because nothing here writes back and the documented requests carry no credentials. Before depending on it, check the 60 second refresh interval against your tolerance for stale data, read the X-Last-Updated-At header on the legacy endpoints to see how old the cache really is, and confirm the ISC license declared in package.json is what you want given the repository license field reads unknown.

Frequently asked questions

Does Twitch have a free API?

twitch-drops-api is a public read-only service that returns Twitch Drops campaigns and reward campaigns over four GET endpoints at twitch-drops-api.sunkwi.com. The documented requests carry no API key, no token and no custom headers, and all four endpoints are backed by one cache refreshed every 60 seconds.

How to automate Twitch drops?

The repository ships fetch-drops.ts and fetch-rewards.ts, wired to the scripts fetch:drops and fetch:rewards, and both run under bun rather than node. drops.json and rewards.json sit at the repository root next to those scripts, with types.ts holding the response types.

What does the v2 response add over the legacy endpoints?

v2 moves freshness into the body through TwitchDropsApiV2ResponseType<T>, which carries lastUpdatedAt, refreshIntervalSeconds and data. The legacy endpoints return the campaign array directly and expose the same freshness through the X-Last-Updated-At and X-Refresh-Interval-Seconds response headers.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sunkwibot-twitch-drops-api.svg)](https://hysenlabs.com/projects/sunkwibot-twitch-drops-api)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/sunkwibot-twitch-drops-api.svg)](https://hysenlabs.com/projects/sunkwibot-twitch-drops-api)

Was this useful?