The gifuu API is publicly accessible and requires no authentication for read and write operations. We recommend that you have the client make requests on their own instead of proxying requests for them to avoid issues with our ratelimits.
Use the following URLs for HTTP requests:
https://gifuu-api.panca.kz/ // Base URL for API Requests
https://gifuu-cdn.panca.kz/ // Base URL for CDN Requests
https://gifuu-cdn.panca.kz/{id}/preview.avif // Up to 200px at 24fpx
https://gifuu-cdn.panca.kz/{id}/standard.avif // Up to 640px at 48fps
https://gifuu-cdn.panca.kz/{id}/alpha.webm // See "Transparency" section belowRatelimit headers are provided with each request:
X-Ratelimit-Bucket // Ratelimit bucket (category) the endpoint uses
X-Ratelimit-Limit // Requests allocated for this bucket per period
X-Ratelimit-Reset // Seconds left until period resets, in float seconds
X-Ratelimit-Remaining // Requests left until 429 errors appear
If you have been blessed with a bypass token provide it with each request using the X-Ratelimit-Token HTTP header.
gifuu stores animations in AV1 format for efficiency. AV1 does not natively support transparency, so we use a stacked video technique to encode the alpha channel alongside the color data.
The alpha.webm file is a double-height video where the top half contains the color data and the bottom half contains the alpha channel encoded as a grayscale luma map. White pixels are fully opaque, black pixels are fully transparent.
To render this correctly you must use a WebGL fragment shader to composite the two halves together. The following shader can be used as a reference:
// Sample color from top half, alpha from bottom half
vec2 colorUV = vec2(vUV.x, vUV.y * 0.5);
vec2 alphaUV = vec2(vUV.x, 0.5 + vUV.y * 0.5);
vec4 color = texture2D(uFrame, colorUV);
float alpha = texture2D(uFrame, alphaUV).r;
gl_FragColor = vec4(color.rgb, alpha);
This technique was pioneered by Jake Archibald — full writeup here. If you don't want to implement this yourself, simply embed our player instead.
<iframe
src="https://gifuu.panca.kz/embed.html?id=123"
width="640"
height="360"
style="border:none"
></iframe>Replace 1273 with the animation ID. The embed handles transparency, playback, and scaling automatically.
The following types are returned as responses across API endpoints:
{
"id": string // Tag ID (Snowflake)
"label": string // Tag Name
"usage": number // Number of animations using this tag
}{
"id": string // Animation ID (Snowflake String)
"created": string // ISO 8601 Timestamp
"width": number // Approximate Width
"height": number // Approximate Height
"sticker": boolean // Animation Static?
"title": string // Animation Title
"tags": Tag[] // Associated Tags
}This site uses tags to make it's database queryable. They should match the following regex (^[\p{L}\p{N}_]+$) or the server will rejecting your request.
Returns the most popular tags (highest usage) on the platform.
Request Body:
{
"limit": number // Amount of results to return (range 1-100)
}Response Body:
Tag[]
Search for tags with a similar spelling.
Request Body:
{
"query": string // Search Query, must match Tag regex.
"limit": number // Amount of results to return (range 1-100)
}Response Body:
Tag[]
Content is processed and served as AVIF files for efficiency. Most modern web browsers and operating systems support this feature.
Returns the latest animations with the following tags.
Request Body:
{
"limit": number, // Amount of results to return (range 1-100)
"after_id": string? // Cursor for pagination (optional)
"query": string[] // Tags to filter by (Snowflake)
}Response Body:
Animation[]
Returns the latest animations uploaded to the site.
Request Body:
{
"limit": number, // Amount of results to return (range 1-100)
"after_id": string? // Cursor for pagination (optional)
}Response Body:
Animation[]
Uploads an animation to the website.
NOTE: This endpoint requires Proof of Work.
NOTE: This request may timeout if the server is busy. Your application should implement some method of automatic retry.
Request Body: [multipart/form-data]
field: data (text)
{
"title": string, // Animation title (range 1-128 characters) (required)
"tags": string[], // Tag Names to attach, plaintext (range 1-32 tags) (required)
"pow_nonce": string, // Nonce from PoW endpoint
"pow_solution": string // Solution
}
field: file (binary)
video/*, image/*
Request Headers:Response Body:
{
"id": string // New Animation ID
"edit_token": string // New Animation Edit Token
// Save this! This token is required to delete the animation!
}Returns metadata for a single animation.
Response Body:
Animation
Deletes an animation, requires the edit token returned at upload time. Pass the token via the Authorization HTTP Header, the server will then respond with a 204 No Content on success.
Additional one-off endpoints.
Returns a single use Proof-of-Work (PoW) token for the client to solve. The difficulty depends on the current vibe and state of affairs.
Provide your completed Counter and given Nonce to endpoints that require PoW via their respective X-Pow-Counter and X-Pow-Nonce headers.
Response Body:
{
"nonce": string, // Hex encoded nonce to use in your hash
"difficulty": number, // Required leading zero bits
"expires": number // UNIX Timestamp
}Example Solver (JavaScript):
const { nonce, difficulty } = await fetch("/pow").then(r => r.json())
const encoder = new TextEncoder()
let counter = 0
while (true) {
const data = encoder.encode(nonce + counter)
const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", data))
let zeroBits = 0
for (const byte of hash) {
if (byte === 0) { zeroBits += 8 }
else { zeroBits += Math.clz32(byte) - 24; break }
}
if (zeroBits >= difficulty) break
counter++
}
// Submit token + counter with your upload