Developer docs

Reaction GIFs for any bot.

Fetch a random gif URL for a topic with a simple Bearer-authenticated GET request.

Base URL
https://mrs-maid.cc/api/v1
Auth scheme
Bearer
Format
JSON
Categories
22

Overview

The GIF API returns a random, directly embeddable GIF URL for a topic. One GET, one bearer key, one JSON response. No session handling, and the client is optional.

Curated library

Every GIF is hosted on our own servers and reviewed per category. No redirects to third-party providers, no dead links.

SFW and NSFW kept apart

The rating is part of the path. Your bot decides per channel what it may request, without filtering the response afterwards.

Random pick

Every request picks a random GIF from the category server-side. You need no list of your own and no randomiser.

Ready to embed

The returned URL points at the GIF file itself and drops straight into a Discord embed.

Quickstart

Three steps from login to your first GIF.

  1. Create a key

    Sign in with Discord and create a GIF API key in the dashboard. The key is shown in clear text exactly once.

    Sign in to get a key

  2. Send your first request

    Put the key in the Authorization header and request a category.

    curl
    curl -H "Authorization: Bearer YOUR_KEY" \
      https://mrs-maid.cc/api/v1/sfw/kiss
  3. Embed the response

    Take the url field from the response and use it as the image in your embed.

    json
    {
      "url": "https://mrs-maid.cc/uploads/gifs/sfw/kiss/<hash>.gif",
      "rating": "sfw",
      "category": "kiss",
      "id": "cm7q1v0f30001s60c9x2r4kd"
    }

Official client

For Node there is @mrs-maid/gif on npm. It speaks the same REST API as this page and handles retries, timeouts and error codes for you. It is optional: every call shown here works just as well with curl or fetch.

@mrs-maid/gif No dependencies · ESM + CJS · Node 18+ · TypeScript types included
npm
npm install @mrs-maid/gif
JavaScript
import { MrsMaidGif } from '@mrs-maid/gif';

const gif = new MrsMaidGif({ apiKey: process.env.MRSMAID_GIF_KEY });

const { url } = await gif.sfw('kiss');
await interaction.reply({ embeds: [{ image: { url } }] });

When the client throws, it throws a GifApiError carrying the same code as the REST response. The table in the Errors section applies one to one.

The key belongs in an environment variable, not in your code.

View on npm

Authentication

Send your key in the Authorization header on every request.

http
Authorization: Bearer mm_gif_YOUR_KEY

A key starts with mm_gif_ followed by 43 base64url characters.

We only store a hash of your key. It cannot be shown again. If you lose it, revoke it and create a new one.

Free allows 1 active keys, Pro allows 3. Each key counts its own daily quota.

If the header is missing or unusable, the API answers 401 with a bearer challenge:

http
WWW-Authenticate: Bearer realm="mrs-maid-gif-api"

The key belongs on your server only. Never put it in client code, a frontend bundle or a public repository.

Endpoints

Four endpoints, all GET. Two are public, two require a key.

GET /api/v1 Public

Service info. Returns name, version, the link to these docs, the available ratings and a short usage hint. No key required.

Example request

curl
curl https://mrs-maid.cc/api/v1

Example response

json
{
  "name": "Mrs. Maid GIF API",
  "version": "v1",
  "docs": "https://mrs-maid.cc/gif-api",
  "ratings": ["sfw", "nsfw"],
  "usage": "GET /api/v1/{rating}/{category} with header \"Authorization: Bearer <key>\""
}
GET /api/v1/endpoints Public

Lists every category that actually holds GIFs, with count and path. Empty categories do not appear. Query it when your bot starts instead of hardcoding the list.

The response is cached for 15 seconds and served with Cache-Control: public, max-age=15. Polling faster does not give you fresher numbers.

Example request

curl
curl https://mrs-maid.cc/api/v1/endpoints

Example response

json
{
  "endpoints": [
    {
      "rating": "sfw",
      "category": "kiss",
      "label": "Kiss",
      "count": 100,
      "path": "/api/v1/sfw/kiss"
    }
  ]
}
GET /api/v1/me Key required No quota used

Shows the prefix and label of your key, your plan and the current quota state. This endpoint does not consume quota itself.

On the unlimited plan, dailyLimit and remaining are null. Treat null as no limit, not as zero requests left.

Because it consumes no quota, this endpoint works well as a health check. Use it at bot startup to verify your key is still valid without burning a request.

Example request

curl
curl -H "Authorization: Bearer YOUR_KEY" \
  https://mrs-maid.cc/api/v1/me

Example response

json
{
  "keyPrefix": "mm_gif_9Kx2Qd",
  "label": "my-bot",
  "plan": "free",
  "quota": {
    "dailyLimit": 1000,
    "used": 42,
    "remaining": 958,
    "resetInSeconds": 38211,
    "day": "2026-07-24"
  }
}

Response on unlimited

json
{
  "keyPrefix": "mm_gif_9Kx2Qd",
  "label": null,
  "plan": "unlimited",
  "quota": {
    "dailyLimit": null,
    "used": 128401,
    "remaining": null,
    "resetInSeconds": 38211,
    "day": "2026-07-24"
  }
}
GET /api/v1/{rating}/{category} Key required Counts against quota

One GET returns a random gif for the given rating and category.

Parameters

Parameter Values Meaning
rating sfw nsfw Decides which pool the GIF is drawn from.
category see categories The topic within the rating.

Example request

curl
curl -H "Authorization: Bearer YOUR_KEY" \
  https://mrs-maid.cc/api/v1/sfw/kiss

Example response

json
{
  "url": "https://mrs-maid.cc/uploads/gifs/sfw/kiss/<hash>.gif",
  "rating": "sfw",
  "category": "kiss",
  "id": "cm7q1v0f30001s60c9x2r4kd"
}

Categories

The slugs are stable and will not change. You can hardcode them in your bot.

SFW (14)

kisscuddleslappunchlovelickheadpathugairkissangrydeadcrykillhorny

NSFW (8)

blowjobfucklesbian_fucklickplayhandjobfeetjobthighs

lick deliberately exists under both ratings. They are two separate pools, and only the rating in the path decides which one you get. Requesting it under the wrong rating returns 404 unknown_category.

Available categories

941 gifs across the categories below. Only categories with gifs are listed.

Topic Rating Path Gifs
Kiss sfw /api/v1/sfw/kiss 100
Cuddle sfw /api/v1/sfw/cuddle 100
Slap sfw /api/v1/sfw/slap 90
Punch sfw /api/v1/sfw/punch 47
Love sfw /api/v1/sfw/love 9
Lick sfw /api/v1/sfw/lick 28
Headpat sfw /api/v1/sfw/headpat 100
Hug sfw /api/v1/sfw/hug 100
Air Kiss sfw /api/v1/sfw/airkiss 6
Angry sfw /api/v1/sfw/angry 32
Cry sfw /api/v1/sfw/cry 13
Blowjob nsfw /api/v1/nsfw/blowjob 100
Fuck nsfw /api/v1/nsfw/fuck 100
Lesbian Fuck nsfw /api/v1/nsfw/lesbian_fuck 100
Lick nsfw /api/v1/nsfw/lick 16

Response headers and rate limits

Three independent layers limit access. Each of them can trigger on its own.

The three layers

Layer Limit Meaning
Per IP 300/min Fixed cap on the authenticated endpoints, independent of the key. It applies before the key lookup runs.
Per key, short window 120/min Burst brake on the GIF endpoint so a single key cannot fire many times per second.
Per key, daily 1,000 / 50,000 The actual daily budget from your plan, Free and Pro. Resets daily at 00:00 UTC.

Headers on the GIF endpoint

Header Meaning
X-RateLimit-Limit Your daily limit for this key.
X-RateLimit-Remaining Requests left for today.
X-RateLimit-Reset Seconds until the reset, not a timestamp. Convert it yourself if you need a clock time.
Retry-After Only on 429. Seconds to wait before you retry.

On an unlimited key these headers are absent entirely, because there is no quota. If your client reads them, treat their absence as no limit rather than zero.

Plans

The daily limit applies per key, not per account. Several keys do not multiply your budget, they only separate the counters.

Free

1,000 / day

  • 1 key(s)
  • All categories, SFW and NSFW
  • Free of charge

Unlimited

No quota

  • 3 key(s)
  • No rate limit headers in the response
  • Granted by the operator only, not purchasable

Quota is counted per key and resets daily at 00:00 UTC. NSFW categories are readable with any valid key.

Errors

Every error comes back as JSON with an error field, sometimes with an additional message. Evaluate the code, not the text.

Status Code Meaning Retry?
401 missing_api_key No Authorization header was sent. No
401 invalid_api_key Key unknown or revoked. No
404 unknown_category The rating or category does not exist, including a category requested under the wrong rating. No
404 no_gifs The category is valid but holds no GIFs yet. No
429 rate_limited Too many requests per minute, from your IP or from this key. Yes, with backoff
429 quota_exceeded The daily quota for this key is used up. See Retry-After. Only after reset
500 internal_error Unexpected error on our side. Retrying is worthwhile. Yes, with backoff
503 unavailable Service temporarily unavailable. Yes, with backoff

404 no_gifs still consumes one request from your daily quota, because the quota is drawn before the lookup runs. 404 unknown_category costs you nothing. So check once at startup via /api/v1/endpoints which categories are filled, instead of hitting empty ones in production.

json
{
  "error": "quota_exceeded",
  "message": "Daily request limit reached for this key.",
  "limit": 1000,
  "resetInSeconds": 38211
}

Usage and embedding

What to know when you wire this up.

CORS is open

Access-Control-Allow-Origin is set to *, and only GET and OPTIONS are allowed. An OPTIONS preflight is answered with 204.

The GIF files are public

URLs under /uploads/gifs/ need no key. You can serve them in an embed or in a browser without exposing your key.

Do not cache

The pick is random, and the same category can return the same GIF twice in a row. Do not cache the response or you will always get the same image.

Even though CORS is open, the key does not belong in the browser. Call the API from your bot or backend and pass on only the finished GIF URL.

AI help

Sign in to ask the API assistant. Sign in to get a key