# Get Analytics Source: https://docs.snipp.gg/api-reference/endpoint/analytics GET /analytics Fetch your own analytics: posts, uploads, data uploaded, comments received, likes received and followers gained over time, plus totals. This is the same data that powers the Analytics page in the Snipp Console, exposed so you can rebuild it in your own tools. ## Behavior * Authenticated with your `api-key`. Returns analytics for the key's owner only. * Requires an active PLUS subscription. Without it the endpoint returns `403`. * `range` selects the window for `series` and `deltas`. Valid values are 7, 30, 90 and 365. It defaults to 30 if omitted or invalid. `buckets` are always today, last 7 days and last 30 days regardless of `range`. * `series` arrays have one entry per day across the window, oldest first, including days with no activity (value `0`). * `totals.posts` and `totals.views` are lifetime; `totals.views` is the sum of views across all your posts. `totals.comments`, `totals.likes` and `totals.bytes` cover the fetch window only, which is twice the selected `range` with a floor of 30 days. * `deltas.change` is the fractional change against the preceding window of equal length. It is `null` when the preceding window was zero and the current one is not, and `0` when both are zero. * All dates are UTC. ## Examples Fetch your analytics for the last 90 days: ```bash theme={null} curl -X GET "https://api.snipp.gg/analytics?range=90" \ -H "api-key: YOUR_API_KEY" ``` ## Responses ```json theme={null} { "analytics": { "range": 30, "buckets": { "posts": { "today": 2, "week": 11, "month": 47 }, "uploads": { "today": 3, "week": 14, "month": 63 }, "bytes": { "today": 1048576, "week": 8388608, "month": 27918336 }, "comments": { "today": 0, "week": 4, "month": 18 }, "likes": { "today": 1, "week": 9, "month": 38 }, "followers": { "today": 1, "week": 6, "month": 21 } }, "totals": { "posts": 666, "views": 1695, "comments": 18, "likes": 412, "bytes": 27918336 }, "series": { "posts": [{ "day": "2026-05-06", "value": 1 }, { "day": "2026-05-07", "value": 0 }], "uploads": [{ "day": "2026-05-06", "value": 2 }, { "day": "2026-05-07", "value": 0 }], "views": [{ "day": "2026-05-06", "value": 84 }, { "day": "2026-05-07", "value": 31 }], "bytes": [{ "day": "2026-05-06", "value": 524288 }, { "day": "2026-05-07", "value": 0 }], "comments": [{ "day": "2026-05-06", "value": 1 }, { "day": "2026-05-07", "value": 0 }], "likes": [{ "day": "2026-05-06", "value": 3 }, { "day": "2026-05-07", "value": 1 }], "followers": [{ "day": "2026-05-06", "value": 2 }, { "day": "2026-05-07", "value": 0 }] }, "deltas": { "posts": { "current": 47, "previous": 41, "change": 0.1463 }, "uploads": { "current": 63, "previous": 55, "change": 0.1454 }, "views": { "current": 1240, "previous": 1080, "change": 0.1481 }, "bytes": { "current": 27918336, "previous": 25000000, "change": 0.1167 }, "comments": { "current": 18, "previous": 22, "change": -0.1818 }, "likes": { "current": 38, "previous": 31, "change": 0.2258 }, "followers": { "current": 21, "previous": 17, "change": 0.2353 } } } } ``` # Add to Album Source: https://docs.snipp.gg/api-reference/endpoint/append POST /appendUpload Add one or more files to an existing album post. Use this to grow a post's file list in place. The post's share code, privacy, title, and description stay the same. ## Headers | Header | Required | Description | | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `api-key` | Yes | Your API key | | `post-code` | Yes | The share code of the post to append to. `code` works as an alias. | | `include-metadata` | No | `true` to keep file metadata. Omitted or `false` strips it (default). | | `priority` | No | `false` to opt appended video files out of priority (adaptive) streaming. Omitted or `true` keeps it on (default), subject to your remaining priority minutes. | ## Behavior * You must own the post. * The post cannot be moderated. Moderated posts reject modifications. * Albums cap at **50 files total**. Requests that would exceed the cap are rejected. * Newly added files inherit the post's existing privacy. If the post is **private**, returned `url`s are signed with a 24-hour expiry. * Video files (`.mp4`, `.mov`, `.mkv`) are remuxed to MP4 with `+faststart`. See [File Types](/concepts/file-types) for details. * Metadata is stripped by default: PNG, JPG, WebP and GIF are stripped structurally at any size, so the pixels are untouched, and videos lose their container metadata during the MP4 remux. Send `include-metadata: true` to keep it. * Appended videos use priority (adaptive) streaming by default, spending [priority minutes](/concepts/plans). Send `priority: false` to add them in standard quality instead. The post's `priority` field is `true` when any file in the album is on adaptive streaming. * Each new file is auto-moderated. If any file (or the post's title or description) is flagged, the entire post, including existing files, is quarantined and marked `moderated`. * **Failures:** when every file in the request fails, the response is `400`. For a single-file upload the reason is returned directly as `error` (for example `Weekly usage limit exceeded.`). For multi-file uploads the response is `All files failed to upload.` with a `failed` array naming each file by `index`. ## Examples Add two images to an existing album: ```bash theme={null} curl -X POST "https://api.snipp.gg/appendUpload" \ -H "api-key: YOUR_API_KEY" \ -H "post-code: AbCd1234" \ -F "file=@photo4.png" \ -F "file=@photo5.png" ``` Convert a single-file post into an album by appending a second file: ```bash theme={null} curl -X POST "https://api.snipp.gg/appendUpload" \ -H "api-key: YOUR_API_KEY" \ -H "post-code: AbCd1234" \ -F "file=@second.png" ``` ## Responses ```json theme={null} { "message": "Files added to album successfully.", "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "fileCount": 5, "priority": false }, "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1920, "height": 1080 } } ] } ``` If some files fail, a `failed` array is included alongside `files`: ```json theme={null} { "failed": [ { "index": 4, "error": "Failed to process file.", "status": "failed" } ] } ``` # Convert a File Source: https://docs.snipp.gg/api-reference/endpoint/convert POST /convert Convert one image or video to another format and get the converted file back in the response. Nothing is stored, no post is created, and no upload quota is consumed. ## Headers | Header | Required | Description | | ---------------- | -------- | ----------------------------------------------------- | | `api-key` | Yes | Your API key | | `convert-target` | Yes | `png`, `jpeg`, `webp`, `avif`, `gif`, `mp4`, or `mp3` | ## Behavior * Send the file as `multipart/form-data`. Only the first file in the request is read. * The response body is the converted file itself, sent as an attachment, not JSON. Write it straight to disk. * The source format is detected from the file's own bytes, not its name or the declared content type. * **What can become what:** PNG, JPG and WebP convert to png, jpeg, webp, avif or gif. GIF converts to png, jpeg, webp, avif or mp4. MP4, MOV and MKV convert to mp4, gif or mp3. * **Video to GIF requires Snipp+** and uses the first 10 seconds only, at 15fps and 480px wide. * **MP4 output** is remuxed with `+faststart` when the source is already H.264, and transcoded otherwise. * **Metadata is always stripped.** Images are re-encoded without it and videos are written with `-map_metadata -1`. JPG orientation is applied to the pixels first, so photos stay the right way up. * Files are capped at 50 MB on the free plan. Snipp+ and Ultra have no per-file cap, the same as [`/upload`](/api-reference/endpoint/upload). Conversion runs inside the request, so a large video can take a while to come back. * Rate limited to 10 conversions per minute. Converting does not count toward your weekly upload limit. * To host the result, pipe it into [`POST /upload`](/api-reference/endpoint/upload). Note that avif and mp3 are not accepted upload types, so those two are download-only. ## Examples Convert a screenshot to WebP: ```bash theme={null} curl -X POST "https://api.snipp.gg/convert" \ -H "api-key: YOUR_API_KEY" \ -H "convert-target: webp" \ -F "file=@screenshot.png" \ -o screenshot.webp ``` Pull the audio out of a clip: ```bash theme={null} curl -X POST "https://api.snipp.gg/convert" \ -H "api-key: YOUR_API_KEY" \ -H "convert-target: mp3" \ -F "file=@clip.mp4" \ -o clip.mp3 ``` Convert, then host the result: ```bash theme={null} curl -X POST "https://api.snipp.gg/convert" \ -H "api-key: YOUR_API_KEY" \ -H "convert-target: mp4" \ -F "file=@loop.gif" \ -o loop.mp4 curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -F "file=@loop.mp4" ``` ## Responses On success the body is the converted file and the headers name it: ``` HTTP/1.1 200 OK Content-Type: image/webp Content-Disposition: attachment; filename="screenshot.webp" Content-Length: 184320 ``` On failure the body is JSON: ```json theme={null} { "error": "That file cannot be converted to this format." } ``` # Delete Upload Source: https://docs.snipp.gg/api-reference/endpoint/delete DELETE /deleteUpload Delete a file from your account by filename. ## Behavior * Pass the file's name in the `file` header. The filename is 32 hex characters plus an extension (for example, `a1b2c3d4e5f6789012345678abcdef01.png`). * Get the filename from the `files[].fileName` field on [`GET /posts/{code}`](/api-reference/endpoint/post) or [`GET /uploads`](/api-reference/endpoint/uploads). * On a single-file post, deleting the file deletes the entire post. * On an album, deleting a file removes just that file. Deleting the last remaining file deletes the album post. * You can only delete your own uploads. ## Examples ```bash theme={null} curl -X DELETE "https://api.snipp.gg/deleteUpload" \ -H "api-key: YOUR_API_KEY" \ -H "file: a1b2c3d4e5f6789012345678abcdef01.png" ``` ## Responses ```json theme={null} { "message": "File deleted successfully." } ``` # Edit Upload Source: https://docs.snipp.gg/api-reference/endpoint/edit PATCH /editUpload Update an existing upload's title, description, or privacy level. ## Behavior * You must own the upload to edit it. * At least one of `title`, `description`, or `post-privacy` must be provided. * Send `title` or `description` as multipart form fields. This is required for non-ASCII values such as emoji or accented characters, which cannot be carried in HTTP headers. The `title` and `description` headers still work for plain ASCII values but are discouraged. * Send an empty string for `title` or `description` to clear the field. * `title` is truncated to 30 characters, `description` to 200 characters. * A restricted post cannot be made public. Setting `post-privacy` to `public` on a restricted post returns `403`. You can still switch it between `private` and `unlisted`. ## Examples Update a post's title and privacy: ```bash theme={null} curl -X PATCH "https://api.snipp.gg/editUpload" \ -H "api-key: YOUR_API_KEY" \ -H "code: AbCd1234" \ -H "post-privacy: public" \ -F "title=New title 🎉" ``` Clear a post's description: ```bash theme={null} curl -X PATCH "https://api.snipp.gg/editUpload" \ -H "api-key: YOUR_API_KEY" \ -H "code: AbCd1234" \ -F "description=" ``` ## Responses ```json theme={null} { "message": "Upload updated successfully.", "post": { "code": "AbCd1234", "title": "New title", "description": null, "postPrivacy": "public" } } ``` # List Public GIFs Source: https://docs.snipp.gg/api-reference/endpoint/gifs GET /gifs Browse public GIFs across Snipp, newest first, like a public GIF feed. Defaults to 30. Pass `?limit=N` (max 50) and `?offset=N` to page. Pass `?search=kitty cat` to match word prefixes in the title or description. ## Behavior * No authentication required. Rate limited per IP. * Only posts whose first file is a `.gif` are returned. * Returns public posts only. Unlisted, private, draft, moderated, and team posts are excluded, as are posts from suspended accounts. * `?search` matches word prefixes (case-insensitive) in the title or description. `kitt` matches "kitty" and "kitten" but not "skittle", since it must match the start of a word. * Multiple words are combined with AND, order independent. `kit cat` matches a post whose title or description has a word starting with "kit" and a word starting with "cat", anywhere. Up to 6 words are used. * `thumbnailUrl` is only present when the post has a thumbnail. * `title` and `description` are `null` when none is set. * `hasMore` is `true` when another page is likely available. Page with `offset`. ## Examples ```bash theme={null} curl -X GET "https://api.snipp.gg/gifs?limit=50&offset=0" ``` ```bash theme={null} curl -X GET "https://api.snipp.gg/gifs?search=kitty" ``` ## Responses ```json theme={null} { "gifs": [ { "code": "AbCd1234", "url": "https://i.snipp.gg/364836696149458944/16483ec5681c5fc2b5cc08dd49b7dbae.gif", "thumbnailUrl": "https://i.snipp.gg/364836696149458944/thumb/16483ec5681c5fc2b5cc08dd49b7dbae.jpg", "title": "Nice loop", "description": null, "created": "2026-03-27T11:42:18.000Z", "views": 128 } ], "hasMore": true, "offset": 0, "limit": 50 } ``` # Get Post Source: https://docs.snipp.gg/api-reference/endpoint/post GET /posts/{code} Fetch a post by its share code. ## Behavior * Albums (posts with more than one file) include `urls` and `isAlbum: true`. * Video posts include `thumbnailUrl`, a generated still frame. For private posts it is a signed URL with the same 24-hour expiry as the file URL. * Use `fileName` from the `files` array when calling [`/deleteUpload`](/api-reference/endpoint/delete) to address a specific file. On albums, `/deleteUpload` removes just that file; on single-file posts it deletes the post. * Liking and commenting are Relay-only actions; see the [Relay Reference](/relay-reference/introduction) to perform them. * Private posts are only visible to the owner. Requests for another user's private post return `404`. * Team posts are only visible to members of that team; everyone else gets a `404`. They omit `likeCount`, since team posts cannot be liked. `commentCount` is still returned. * URLs for private posts are returned as signed URLs with a 24-hour expiry. After that, request the endpoint again for a fresh signed URL. * Moderated posts are only fully returned to their owner, with `moderated: true` on the post. Requests by anyone else return `403` with `moderated: true`. * Restricted posts carry `restricted: true` on the post when returned to their owner. A restricted post is forced to `unlisted` and cannot be set back to public; see [Edit Upload](/api-reference/endpoint/edit). * `priority` is `true` when the post's video is served over priority delivery, and `false` for standard delivery. ## Examples Fetch a post by code: ```bash theme={null} curl -X GET "https://api.snipp.gg/posts/AbCd1234" \ -H "api-key: YOUR_API_KEY" ``` ## Responses Single-file post: ```json theme={null} { "post": { "code": "AbCd1234", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "width": 1920, "height": 1080, "mime_type": "image/png", "size": 2487312, "size_formatted": "2.37 MB" } ], "title": "Nice shot", "description": null, "postPrivacy": "public", "created": "2026-05-03T12:00:00Z", "views": 128, "likeCount": 42, "commentCount": 3, "priority": false, "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } } } } ``` Album post (includes `urls` and `isAlbum`): ```json theme={null} { "post": { "code": "AbCd1234", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "urls": [ "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png" ], "isAlbum": true, "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "width": 1920, "height": 1080, "mime_type": "image/png", "size": 2487312, "size_formatted": "2.37 MB" }, { "index": 1, "fileName": "b2c3d4e5f6789012345678abcdef0102.png", "url": "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png", "width": 1280, "height": 720, "mime_type": "image/png", "size": 1843210, "size_formatted": "1.76 MB" } ], "title": "Match highlights", "description": null, "postPrivacy": "public", "created": "2026-05-03T12:00:00Z", "views": 128, "likeCount": 42, "commentCount": 3, "priority": false, "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } } } } ``` # Get Region Source: https://docs.snipp.gg/api-reference/endpoint/region GET /region Returns which region served the request. Useful for confirming you're hitting the regional endpoint you intended, or for debugging routing through `api.snipp.gg`. ## Behavior * No authentication required. * `region` is the short region code (for example, `eu-west-1` or `us-west-1`). Returns `unknown` if the server has no region configured. * `country` is the country name mapped from the region code (for example, `United Kingdom`). `null` if no country mapping is available. * `label` is a human-readable form (for example, `United Kingdom (eu-west-1)`). Falls back to the raw region code if no country mapping is available. ## Examples Confirm EU West is responding: ```bash theme={null} curl -X GET "https://eu-west-1.api.snipp.gg/region" ``` Check which region `api.snipp.gg` routed you to: ```bash theme={null} curl -X GET "https://api.snipp.gg/region" ``` ## Responses ```json theme={null} { "region": "eu-west-1", "country": "United Kingdom", "label": "United Kingdom (eu-west-1)" } ``` # Report a Post Source: https://docs.snipp.gg/api-reference/endpoint/report-post POST /report-post Report a post for violating community guidelines. Reports are reviewed by the moderation team. ## Endpoint ### Report a Post ``` POST /report-post ``` Submit a report for a post using its share code. An optional reason can be provided to help moderators review the report. **Request body:** ```json theme={null} { "code": "AbCd1234", "reason": "This post contains inappropriate content" } ``` | Field | Type | Required | Description | | -------- | ------ | -------- | ------------------------------------------ | | `code` | string | Yes | The share code of the post to report | | `reason` | string | No | Reason for the report (max 200 characters) | **Response:** ```json theme={null} { "success": true } ``` ## Errors | Code | Error | When | | ----- | -------------------------------------------- | ------------------------------------------------ | | `400` | Missing or invalid post code. | `code` is empty or missing | | `400` | Reason cannot exceed 200 characters. | `reason` exceeds 200 characters | | `403` | This post has already been moderated. | The post has already been actioned by moderators | | `403` | You cannot report your own post. | `code` belongs to a post you own | | `403` | You cannot report this post. | The post author has blocked you | | `404` | Post not found. | Post does not exist, or is private or a draft | | `429` | Rate limit exceeded. Please try again later. | Too many requests | ## Examples Report a post: ```bash theme={null} curl -X POST "https://api.snipp.gg/report-post" \ -H "api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"code": "AbCd1234", "reason": "Inappropriate content"}' ``` Report a post without a reason: ```bash theme={null} curl -X POST "https://api.snipp.gg/report-post" \ -H "api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"code": "AbCd1234"}' ``` ## Responses ```json theme={null} { "success": true } ``` # Report a User Source: https://docs.snipp.gg/api-reference/endpoint/report-user POST /report-user Report a user for violating community guidelines. Reports are reviewed by the moderation team. ## Endpoint ### Report a User ``` POST /report-user ``` Submit a report for a user using their ID. An optional reason can be provided to help moderators review the report. **Request body:** ```json theme={null} { "targetUserId": "123456789012345678", "reason": "This user is impersonating someone else" } ``` | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------ | | `targetUserId` | string | Yes | The ID of the user to report | | `reason` | string | No | Reason for the report (max 200 characters) | **Response:** ```json theme={null} { "success": true } ``` ## Errors | Code | Error | When | | ----- | -------------------------------------------- | --------------------------------------------------- | | `400` | Missing or invalid user. | `targetUserId` is empty, missing, or not a valid ID | | `400` | Reason cannot exceed 200 characters. | `reason` exceeds 200 characters | | `403` | This user has already been suspended. | The user has already been actioned by moderators | | `403` | You cannot report yourself. | `targetUserId` is your own ID | | `403` | You cannot report this user. | The reported user has blocked you | | `404` | User not found. | The user does not exist | | `429` | Rate limit exceeded. Please try again later. | Too many requests | ## Examples Report a user: ```bash theme={null} curl -X POST "https://api.snipp.gg/report-user" \ -H "api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"targetUserId": "123456789012345678", "reason": "Impersonation"}' ``` Report a user without a reason: ```bash theme={null} curl -X POST "https://api.snipp.gg/report-user" \ -H "api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"targetUserId": "123456789012345678"}' ``` ## Responses ```json theme={null} { "success": true } ``` # Upload File(s) Source: https://docs.snipp.gg/api-reference/endpoint/upload POST /upload Upload between 1 and 50 files in a single request. Every upload automatically creates a post. When sending multiple files, they are grouped into an album by default. ## Headers | Header | Required | Description | | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `api-key` | Yes | Your API key | | `post-privacy` | No | `public`, `unlisted`, or `private` (default) | | `post-title` | No | Post title, max 30 characters | | `post-description` | No | Post description, max 200 characters | | `post-type` | No | `album` (default) or `individual`. Only applies to two or more files. | | `include-metadata` | No | `true` to keep file metadata. Omitted or `false` strips it (default). | | `priority` | No | `false` to opt video uploads out of priority (adaptive) streaming. Omitted or `true` keeps it on (default), subject to your remaining priority minutes. | ## Behavior * **One file:** creates a single post. `post-type` is ignored. * **Two or more files, no header or `album`:** creates one album post containing all files. * **Two or more files, `individual`:** creates a separate post for each file. * **`post-privacy: private`:** the returned `url` (and any `urls` in albums) is a signed URL with a 24-hour expiry. After it expires, call [`GET /posts/{code}`](/api-reference/endpoint/post) or [`GET /uploads`](/api-reference/endpoint/uploads) to get a fresh signed URL. * **Video uploads:** `.mp4`, `.mov`, and `.mkv` files are remuxed to MP4 with `+faststart` so the browser can begin playback before the full file has downloaded. `.mov` and `.mkv` uploads are stored (and returned) with an `.mp4` extension. If remuxing fails, the file is rejected as corrupt or unplayable. See [File Types](/concepts/file-types) for details. * **Metadata:** Metadata (camera, GPS location, timestamp) is stripped by default. For images, metadata is removed from PNG, JPG, WebP and GIF at any size, without re-encoding, so the pixels are untouched. Display orientation is preserved on JPG and WebP, and GIF animation and looping are preserved. For videos, container metadata is removed during the MP4 remux. Send `include-metadata: true` to keep it. * **Priority streaming:** Eligible videos are streamed with adaptive bitrate by default, spending [priority minutes](/concepts/plans). Send `priority: false` to upload in standard quality instead and save your minutes. The post's `priority` field reflects whether the video ended up on adaptive streaming. * **Failures:** when every file in the request fails, the response is `400`. For a single-file upload the reason is returned directly as `error` (for example `Weekly usage limit exceeded.`). For multi-file uploads the response is `All files failed to upload.` with a `failed` array naming each file by `index`. ## Examples Upload a single file: ```bash theme={null} curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -H "post-privacy: unlisted" \ -H "post-title: my gaming POV" \ -F "file=@screenshot.png" ``` Upload an album (two or more files default to album): ```bash theme={null} curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -H "post-privacy: public" \ -H "post-title: Game Moderation Evidence" \ -F "file=@photo1.png" \ -F "file=@photo2.png" \ -F "file=@photo3.png" ``` Upload multiple files as individual posts: ```bash theme={null} curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -H "post-type: individual" \ -F "file=@image1.png" \ -F "file=@image2.png" ``` ## Responses Single file: ```json theme={null} { "message": "Upload successful!", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } }, "processing_time": 412, "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "unlisted", "priority": false } } ``` `priority` is `true` when the upload is a video that was placed on priority (adaptive) streaming, and `false` otherwise. Album (two or more files, default): one `post` containing every file. ```json theme={null} { "message": "Upload successful!", "files": [ { "index": 0, "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1920, "height": 1080 } }, { "index": 1, "url": "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png", "size": 1840221, "size_formatted": "1.76 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1280, "height": 720 } } ], "processing_time": 938, "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "isAlbum": true, "fileCount": 2, "priority": false } } ``` Individual (`post-type: individual`): one entry in `post.posts` per file. If a file fails, a `failed` array is included alongside `files` and the message counts what landed. ```json theme={null} { "message": "1 of 2 files uploaded successfully.", "files": [ { "index": 0, "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success" } ], "failed": [ { "index": 1, "error": "Weekly usage limit exceeded.", "status": "failed" } ], "processing_time": 938, "post": { "posts": [ { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "priority": false } ] } } ``` # List Uploads Source: https://docs.snipp.gg/api-reference/endpoint/uploads GET /uploads Fetch your most recent uploads. Defaults to 30. Pass `?limit=N` to control the count (max 1000). ## Behavior * Returns uploads newest first. * Each upload includes `code` for the associated post when one exists. * `isAlbum` is `true` when that upload belongs to an album post. * `title` is the post's title, or `null` when none is set. * `thumbnailUrl` is only present when the post has a thumbnail. * `priority` is `true` when the post's video is served over priority delivery, and `false` for standard delivery. * URLs for private uploads are returned as signed URLs with a 24-hour expiry. After that, request the endpoint again for a fresh signed URL. ## Examples ```bash theme={null} curl -X GET "https://api.snipp.gg/uploads?limit=100" \ -H "api-key: YOUR_API_KEY" ``` ## Responses ```json theme={null} { "uploads": [ { "code": "AbCd1234", "isAlbum": true, "url": "https://i.snipp.gg/364836696149458944/16483ec5681c5fc2b5cc08dd49b7dbae", "thumbnailUrl": "https://i.snipp.gg/364836696149458944/thumb/16483ec5681c5fc2b5cc08dd49b7dbae.jpg", "title": "Nice shot", "size": 2487312, "size_formatted": "2.37 MB", "uploaded": "2026-03-27T11:42:18.000Z", "priority": false } ] } ``` # Get Usage History Source: https://docs.snipp.gg/api-reference/endpoint/usage-history GET /usage-history Fetch your daily upload activity for the last 365 days, along with aggregate stats. This is the same data that powers the Upload Activity heatmap in the Snipp Console, exposed so you can rebuild it in your own tools. ## Behavior * Authenticated with your `api-key`. Returns activity for the key's owner only. * `history` is ascending by date. Days with no uploads are omitted, so the array is sparse. * `bytes` and `count` reflect successful uploads. A reservation that fails mid-upload is rolled back and does not appear. * `stats.currentStreak` counts consecutive days with uploads ending on the current UTC day. It is `0` if you have not uploaded today. * `stats.biggestDay` is `null` when there is no activity in the window. * All dates are UTC. ## Examples Fetch your usage history: ```bash theme={null} curl -X GET "https://api.snipp.gg/usage-history" \ -H "api-key: YOUR_API_KEY" ``` ## Responses ```json theme={null} { "history": [ { "day": "2026-05-12", "bytes": 5242880, "count": 3 }, { "day": "2026-05-14", "bytes": 1048576, "count": 1 } ], "stats": { "totalBytes": 6291456, "totalUploads": 4, "activeDays": 2, "currentStreak": 1, "biggestDay": { "day": "2026-05-12", "bytes": 5242880 }, "windowDays": 365 } } ``` # Get User Source: https://docs.snipp.gg/api-reference/endpoint/users GET /users/{id} Fetch either your own profile (`@me`) or another user's public profile. ## Behavior * `id=@me` returns your full account profile, including `apiKey`, `keyHasUploadsAccess`, `limits`, and `uploads`. * `id=` returns a public-safe profile for that user. * `badges` is always included and follows this order: `verified`, `staff`, `partner`, `bugHunterTier`, `translator`, `plus`. * `bugHunterTier` is an integer from `0` to `2`: `0` is no badge, `1` is Bug Hunter, `2` is Elite Bug Hunter. * `includePosts` controls `publicUploads`. * `includePosts` defaults to `false` for `@me` and `true` for non-self requests. * `postsLimit` limits `publicUploads` to `1` to `50` (default `50`). * Non-self profiles include a `blockedByYou` boolean indicating whether you have blocked that user. * If the target user has blocked **you**, the endpoint returns `403` with `"You have been blocked by this user."`. * If the target user is suspended, the endpoint returns `403` with `suspended: true` and their `username`, and no profile fields. This is checked before the block check. ## Examples Fetch your own profile: ```bash theme={null} curl -X GET "https://api.snipp.gg/users/@me" \ -H "api-key: YOUR_API_KEY" ``` Fetch another profile with a smaller public uploads payload: ```bash theme={null} curl -X GET "https://api.snipp.gg/users/USER_ID?includePosts=true&postsLimit=10" \ -H "api-key: YOUR_API_KEY" ``` ## Responses `@me` returns your full account profile, including `apiKey`, `keyHasUploadsAccess`, and `limits`. ```json theme={null} { "user": { "id": "123456789012345678", "username": "playerone", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "banner": "https://i.snipp.gg/banners/123456789012345678/banner.png", "nickname": "Player One", "bio": "Building things at Snipp", "socials": { "github": "https://github.com/playerone" }, "plus": true, "ultra": false, "verified": false, "staff": false, "partner": false, "bugHunterTier": 0, "translator": false, "suspended": false, "created": "2026-01-04T09:30:00Z", "customEmbed": null, "apiKey": "aB3xK9mP2qR7sT1vW4yZ6cD8fG0hJ5kL", "keyHasUploadsAccess": true, "uploads": 42, "badges": { "verified": false, "staff": false, "partner": false, "bugHunterTier": 0, "translator": false, "plus": true }, "limits": { "plan": "plus", "maxFileSize": null, "usage": { "used": 5242880, "limit": 21474836480, "usedPercent": 0, "windowStart": "2026-05-12T00:00:00Z", "windowEnd": "2026-05-19T00:00:00Z", "resetsInSeconds": 345600 } } } } ``` A non-self request returns a public-safe profile with `blockedByYou` and (by default) `publicUploads`. ```json theme={null} { "user": { "id": "987654321098765432", "username": "playertwo", "avatar": "https://i.snipp.gg/avatars/987654321098765432/avatar.png", "banner": null, "nickname": "Player Two", "bio": null, "socials": null, "plus": false, "ultra": false, "verified": false, "staff": false, "partner": false, "bugHunterTier": 1, "translator": false, "created": "2026-02-18T14:00:00Z", "suspended": false, "customEmbed": null, "badges": { "verified": false, "staff": false, "partner": false, "bugHunterTier": 1, "translator": false, "plus": false }, "blockedByYou": false, "publicUploads": [ { "url": "https://i.snipp.gg/987654321098765432/a1b2c3d4e5f6789012345678abcdef01.png", "code": "AbCd1234", "title": "Launch teaser", "description": null, "created": "2026-02-19T10:00:00Z", "public": true, "isAlbum": false } ] } } ``` A suspended user returns `403` with no profile fields. ```json theme={null} { "error": "This user has been suspended indefinitely for violating our Terms of Service.", "suspended": true, "username": "playertwo" } ``` # API Overview Source: https://docs.snipp.gg/api-reference/introduction Base URL, authentication, endpoints, and error handling for the Snipp API. ## Overview The Snipp API is the public, third-party integration surface. Use it to upload media, manage your files, fetch posts, and build apps and SDKs on top of Snipp. For first-party clients (for example, the Snipp mobile app) that need account-level features such as the discover feed, likes, comments, blocking, and profile editing, use the [Relay API](/relay-reference/introduction) instead. ## Base URL The default base URL is: ``` api.snipp.gg ``` `api.snipp.gg` is a load balancer sitting in front of the upload servers. It resolves to the nearest healthy region and connects straight to it, with no per-request size cap, so your file size limit is whatever your plan allows. It is the right default for everything, including uploads. ### Regional endpoints | Region | Host | | ------- | ------------------------ | | EU West | `eu-west-1.api.snipp.gg` | | US West | `us-west-1.api.snipp.gg` | Regional endpoints reach the same servers as `api.snipp.gg` and upload at the same speed, so pinning one buys you no performance. Use them when you want to control which region stores your files, or to force a specific server instead of letting the load balancer choose. To see which region you are being routed to, call [`GET /region`](/api-reference/endpoint/region) on any host. ```bash theme={null} curl -s https://api.snipp.gg/region ``` ### Alternative endpoint `flux.snipp.gg` serves the same API over a separate network path. Requests are buffered before they reach the server, so uploads there are capped at **100 MB per request** and large uploads are several times slower. Use it only as a fallback if `api.snipp.gg` is ever unreachable. For normal use, stay on `api.snipp.gg`. ## Authentication Include your API key in the `api-key` header with every request. See [Authentication](/authentication) for details on locating your key and security best practices. ``` api-key: YOUR_API_KEY ``` ## Endpoints | Method | Endpoint | Description | | -------- | ---------------- | ---------------------------------------------------------------- | | `GET` | `/users/{id}` | Fetch a user profile. Use `@me` for your own account. | | `GET` | `/posts/{code}` | Fetch a single post by share code. | | `POST` | `/upload` | Upload an image or video file. | | `GET` | `/uploads` | List your 30 most recent uploads. | | `PATCH` | `/editUpload` | Update title, description, or privacy of a post. | | `POST` | `/appendUpload` | Add files to an existing album. | | `DELETE` | `/deleteUpload` | Delete a file or post. | | `POST` | `/report-post` | Report a post. | | `POST` | `/report-user` | Report a user. | | `GET` | `/gifs` | Browse public GIFs across Snipp, newest first. No auth required. | | `GET` | `/usage-history` | Fetch your daily upload activity for the last 365 days. | | `GET` | `/analytics` | Fetch your analytics over time plus lifetime totals. PLUS only. | | `GET` | `/region` | Return the region serving the request. No auth required. | Looking for the discover feed, likes, comments, blocking, profile editing, avatar uploads, or anything that powers the full Snipp experience? See the [Relay API](/relay-reference/introduction). ## Rate Limits Rate limits apply per endpoint, not per account. See [Rate Limits](/concepts/rate-limits) for the full table and details on handling `429` responses. ## Error Format All errors return a JSON object with an `error` field: ```json theme={null} { "error": "Unauthorized. No api-key header found." } ``` ## HTTP Status Codes | Code | Meaning | | ----- | --------------------------------------------------------------------- | | `200` | Success | | `400` | Bad request. Invalid input, unsupported file type, or file too large. | | `401` | Unauthorized. Missing or invalid API key. | | `403` | Forbidden. Account suspended or insufficient permissions. | | `404` | Not found | | `413` | Payload too large. File exceeds your plan's size limit. | | `429` | Rate limit exceeded | | `500` | Internal server error | # Authentication Source: https://docs.snipp.gg/authentication Authenticate all API requests using your API key. ## API Key Authentication All Snipp API endpoints require authentication. Include your API key in the `api-key` header with every request. ``` api-key: YOUR_API_KEY ``` ### Where to Find Your API Key 1. Sign in to your account. 2. Go to **Settings**. 3. Copy your API key from the **API** section. ### Examples ```bash cURL theme={null} curl -X GET "https://api.snipp.gg/users/@me" \ -H "api-key: YOUR_API_KEY" ``` ```javascript JavaScript theme={null} const res = await fetch("https://api.snipp.gg/users/@me", { headers: { "api-key": "YOUR_API_KEY" }, }); const data = await res.json(); ``` ```python Python theme={null} import requests res = requests.get( "https://api.snipp.gg/users/@me", headers={"api-key": "YOUR_API_KEY"}, ) data = res.json() ``` ### API Key Permissions Every key can upload. Reaching the uploads already in your gallery is a separate permission, off by default: | Permission | Description | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Base** | Upload files, fetch your profile, read posts, browse Discover. Always enabled. | | **Gallery** | List, edit, append to, and delete uploads you already have. Off by default. Turn it on with the **Gallery Access** switch in the API section of settings. | Without gallery access, `GET /uploads`, `PATCH /editUpload`, `POST /appendUpload` and `DELETE /deleteUpload` return `403`. `POST /upload` is unaffected. You can check whether gallery access is enabled by calling `GET /users/@me` and reviewing the `keyHasUploadsAccess` field. The field name is historical and reports gallery access. ### Team Access Keys [Teams](/concepts/plans#teams) (an Ultra feature) each have a dedicated **access key**. A team key works exactly like a personal API key (pass it in the `api-key` header), but uploads made with it go to the team's shared gallery instead of a personal account. ``` api-key: YOUR_TEAM_ACCESS_KEY ``` Key differences when uploading with a team key: * Files are stored in the **team gallery**, not a personal gallery. * Uploads count against the **team's** weekly quota, never a member's personal quota. * Team-key uploads are always created **private**, regardless of the `post-privacy` header, and their privacy cannot be changed afterward. Team posts are never **unlisted** or **public**. * Only the team owner can regenerate or delete the key, from the team's settings page. * If the team's subscription lapses, uploads with the key return `403`. Reading, editing, and deleting existing team posts keep working. The same key also works on the [Relay API](/relay-reference/introduction), passed in its `relay-key` header, on `/upload`, `/appendUpload`, `/editUpload`, and `/deleteUpload`. Anyone who holds a team key can upload with it, so treat it like any other credential and share it only with trusted team members. ### Security Best Practices * **Never expose your API key** in client-side code, public repositories, or shared documents. * **Use environment variables** in production (for example, `process.env.SNIPP_API_KEY`). * **Rotate your key** immediately if you suspect it has been compromised. You can regenerate it from your account settings. * **Limit upload access** if your integration only needs read access. ### Error Responses | Status | Cause | | ------ | --------------------------------------------- | | `401` | Missing `api-key` header | | `401` | Invalid or expired API key | | `403` | Account suspended or insufficient permissions | All errors return a JSON object with an `error` field describing the issue. # Custom Domains Source: https://docs.snipp.gg/concepts/custom-domains Use your own domain for Snipp post links. ## Overview You can serve Snipp post links from your own domain by setting up a redirect from `example.com/[code]` to `snipp.gg/p/[code]`. This is a DNS-level redirect that you configure with your own domain provider. Snipp does not host your domain. Because the redirect catches every path on your domain, your custom domain should be dedicated to Snipp. Do not point a domain you also use for a website or other services. The address bar will switch to `snipp.gg` after the redirect completes. Snipp does not currently offer true custom hostnames where the URL stays on your domain. Custom domains are available on every plan, including Free. There is nothing to enable in your account settings, since the redirect is configured entirely on your own domain. If you are a PLUS Subscriber, you can pair a custom domain with the **Custom Embeds** feature to fully personalize how your posts appear when shared. When combined, the two features let you present uploads under your own brand and design rather than the default Snipp look. We are working on the ability to set a custom domain as your default link domain across Snipp. Once shipped, copied links and shared URLs will use your chosen domain automatically, so you will not have to swap `snipp.gg` for your domain by hand each time. ## Using a Custom Domain in ShareX If you upload through [ShareX](https://getsharex.com) (or [XerahS](https://xerahs.com) on macOS), you can already make your custom uploader return links on your own domain by editing the configuration file. Open your `.sxcu` (or `.iscu`) file and change the `URL` field to: ``` https://example.com/{json:post.code} ``` Replace `example.com` with your custom domain. ShareX will copy the new URL format directly after every upload. ## Setup with Cloudflare Cloudflare offers two ways to do this. Either works for a single-domain vanity setup. Pick whichever you are more familiar with. ### Option A: Bulk Redirects (Recommended) [Bulk Redirects](https://developers.cloudflare.com/rules/url-forwarding/bulk-redirects/) are the newer, list-based equivalent. Free on every plan and not limited to 3 entries. 1. Go to your Cloudflare dashboard → **Bulk Redirects**. 2. Create a new list with a redirect rule: | Field | Value | | -------------------- | ---------------------- | | Source URL | `https://example.com/` | | Target URL | `https://snipp.gg/p/` | | Status | `301` | | Preserve path suffix | Enabled | 3. Attach the list to your zone. ### Option B: Page Rules [Page Rules](https://developers.cloudflare.com/rules/page-rules/) are the simplest one-rule option. The Free plan includes 3 page rules per zone. 1. Go to your Cloudflare dashboard → **Rules** → **Page Rules**. 2. Create a new rule: | Field | Value | | --------------- | ------------------------- | | URL match | `example.com/*` | | Setting | **Forwarding URL** | | Status code | `301 Permanent Redirect` | | Destination URL | `https://snipp.gg/p/${1}` | 3. Save and deploy. Cloudflare is gradually migrating Page Rules to the newer Rules engine. Existing rules continue to work, but new setups may want Bulk Redirects for longevity. Either way, a visit to `example.com/[code]` will resolve to `snipp.gg/p/[code]`. ## Setup with Other Providers Most DNS providers support URL forwarding with path preservation. Look for: * **Namecheap:** Domain List → Manage → Redirect Domain (with "Path forwarding" enabled). * **Porkbun:** URL Forwarding → enable "Include path". * **GoDaddy:** Domain Settings → Forwarding → "Forward with masking" off, "Update my nameservers" off. Configure forwarding from `https://example.com/` to `https://snipp.gg/p/` with path preservation enabled. If your provider does not support path-preserving forwarding, point the domain at a small worker, redirect script, or static host that issues a `301` to `https://snipp.gg/p/{code}`. A redirect lives entirely on your domain's DNS. Snipp has no way to know which domain redirects to which post, and it cannot stop another person from configuring a redirect that targets the same Snipp link. Treat the custom domain as a vanity layer, not a security boundary. Snipp does not offer hostname verification (TXT records, dashboard claims, etc.) for this feature. Even with verification, a third party could still point their own domain at the same `snipp.gg/p/[code]` URL. Verification would only confirm that you control your domain, not that you control who else can redirect to a public Snipp link. The end result is the same as the unverified setup, so the extra step would add friction without adding protection. # File Types Source: https://docs.snipp.gg/concepts/file-types Supported file formats and size limits. ## Supported Formats Snipp supports a range of image and video formats for uploading and sharing. ### Images | Format | Extension | MIME Type | | ------ | --------------- | ------------ | | PNG | `.png` | `image/png` | | JPEG | `.jpg`, `.jpeg` | `image/jpeg` | | WebP | `.webp` | `image/webp` | | GIF | `.gif` | `image/gif` | ### Video | Format | Extension | MIME Type | | ------ | --------- | ------------------ | | MP4 | `.mp4` | `video/mp4` | | MOV | `.mov` | `video/quicktime` | | MKV | `.mkv` | `video/x-matroska` | All uploaded videos are automatically remuxed to MP4 with the `moov` atom moved to the start of the file (faststart). This allows browsers to begin playback before the full file has downloaded. `.mov` and `.mkv` uploads are stored as `.mp4` after processing, and the share URL reflects the new extension. If remuxing fails, the file is rejected as corrupt or unplayable. ## Discord Bot Uploads The Snipp Discord bot is invite-only. Add it to your server or as a user app from [snipp.gg/bot](https://snipp.gg/bot). There is no HTTP endpoint for the bot. The bot accepts images only (`.png`, `.jpg`, `.webp`, `.gif`). Videos are **not supported** through the bot. Upload those from the [web uploader](https://snipp.gg/upload) or the [`/upload` endpoint](/api-reference/endpoint/upload). ## File Size Limits Maximum file size depends on your plan: | Plan | Max File Size | | ----- | ----------------- | | Free | 50 MB | | PLUS | No per-file limit | | Ultra | No per-file limit | PLUS and Ultra have no per-file cap. Individual uploads are only bounded by your weekly quota (20 GB on PLUS, 50 GB on Ultra). The absolute upload limit is **50 GB per file**. Files larger than your plan's limit return a `413` error. Videos have a hard ceiling of **30 GB** per file on PLUS and Ultra; larger videos are rejected. Free accounts are still bound by the 50 MB per-file cap above. Upgrading to [PLUS](https://snipp.gg/pricing) removes the 50 MB per-file cap. Upload anything up to your full weekly quota in a single file. ## Unsupported Files If you upload a file with an unsupported format, the API returns a `400` error: ```json theme={null} { "error": "Unsupported file type." } ``` ## Reserved Marker Files that begin with an internal encryption marker are rejected by the upload endpoint. If you encounter this error, the file may be corrupted or use a reserved byte sequence. Try re-exporting the file from its original source. # Plans Source: https://docs.snipp.gg/concepts/plans Compare Free, PLUS, and Ultra plan features and limits. ## Plan Tiers Snipp offers three tiers to suit different needs. All plans include full API access. | | Free | PLUS | Ultra | | ---------------------- | ----------------------- | ----------------------- | ----------------------- | | **Price** | Free | \$4.99/mo | \$49/mo | | **Max File Size** | 50 MB | No per-file limit | No per-file limit | | **Weekly Usage Limit** | 2 GB | 20 GB | 50 GB | | **Upload Rate** | 20 uploads / 30 seconds | 20 uploads / 30 seconds | 20 uploads / 30 seconds | | **Priority Streaming** | 10 min/month | 100 min/month | 300 min/month | | **API Access** | Yes | Yes | Yes | | **Teams** | No | No | Yes | | **PLUS Badge** | No | Yes | Yes | | **Priority Support** | No | Yes | Yes | ## Weekly Usage Limit Each plan has a 7-day upload quota. Your account is assigned a fixed reset day and time when you sign up, and that slot never moves. Once a week, at that exact moment, the entire counter returns to zero. The window does not slide, and it does not depend on when you upload. It is not a continuous tally of the last seven days, and an idle week neither stacks quota nor delays your reset. Unused allowance does not carry over. You can see your next reset in Settings under Billing. Failed uploads do not count against the limit. Only successfully stored files consume bytes. Deleting a file does not restore weekly quota. The limit measures upload throughput per window, not stored bytes, so any successful upload counts for the full window even if the file is later removed. ## Priority Minutes Every account gets **priority streaming** for short videos. Priority videos are served with adaptive bitrate (HLS), which means fast seeking and multiple quality levels from 240p up to the source resolution. Once the budget is spent, videos fall back to standard quality, served directly from storage with no adaptive bitrate. Each plan gets a monthly budget of priority minutes: | Plan | Priority Minutes | | ----- | ----------------- | | Free | 10 minutes/month | | PLUS | 100 minutes/month | | Ultra | 300 minutes/month | A video's **duration** counts against the budget, not its file size. Only videos use priority minutes; images never do. There is a **60-minute** per-video cap. Videos longer than 60 minutes always play in standard quality regardless of how much budget you have left. The priority-minutes budget uses a 30-day window on a fixed schedule: your account is assigned a reset date and time at signup, and the full budget returns every 30 days at that moment whether or not you stream. When your monthly budget runs out, videos still upload and play normally. They simply fall back to standard quality instead of priority streaming, with no block or error. ## Checking Your Plan You can view your current plan and weekly usage in three places: * **Website:** the Settings page shows your plan, max file size, weekly usage bar, and reset time. * **Discord bot:** run `/usage` for the same view inside Discord. See [Discord Bot](/discord-bot/overview). * **API:** call `GET /users/@me`. ## Checking Your Plan via the API Call `GET /users/@me` to see your current plan status and weekly storage usage. The response includes: ```json theme={null} { "user": { "plus": true, "ultra": false, "uploads": 42, "limits": { "plan": "plus", "maxFileSize": null, "usage": { "used": 1073741824, "limit": 21474836480, "usedPercent": 5, "windowStart": "2026-05-01T00:00:00.000Z", "windowEnd": "2026-05-08T00:00:00.000Z", "resetsInSeconds": 172800 } } } } ``` | Field | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------- | | `plus` | `true` if you have an active PLUS subscription | | `ultra` | `true` if you have an Ultra plan | | `uploads` | Total number of files you have uploaded | | `limits.plan` | Active plan (`free`, `plus`, or `ultra`) | | `limits.maxFileSize` | Maximum size per file in bytes. `null` on PLUS and Ultra (no per-file limit; only weekly quota applies) | | `limits.usage.used` | Bytes uploaded in the current window | | `limits.usage.limit` | Total bytes allowed per window | | `limits.usage.usedPercent` | Percentage of the weekly cap consumed, rounded to the nearest integer (0–100+) | | `limits.usage.windowStart` | When the current window started, derived from the account's fixed reset schedule | | `limits.usage.windowEnd` | When the current window resets: the account's fixed reset moment, the same day and time every week | | `limits.usage.resetsInSeconds` | Seconds until the current window resets | ## Teams Ultra accounts can create **Teams**: shared workspaces with their own gallery, upload quota, and access key. * An Ultra subscription covers **1 team**. Ultra plans can be issued with a higher team count. * Each team holds up to **10 members** by default, including the owner. Ultra plans can be issued with a different seat count. * Only **Ultra** users can create a team. Members can be on any plan, including Free: the team's subscription covers every seat, and uploads to a team use the team's limits rather than the member's own. * A user can belong to at most **10 teams**, and each team can hold at most **5** active invite links at a time. * Each team has its own 7-day quota, separate from any member's personal quota: **20 GB per member**, with a floor of **50 GB**. A team of 3 gets 60 GB per week, a full team of 10 gets 200 GB. It follows the same fixed-schedule model: the team has its own reset day and time, and the full quota returns at that moment every week. Uploads to a team never count against a member's personal limit. * Team posts are always **private**, and their privacy cannot be changed afterward. They never appear on Discover, and have no likes or views. * Only members of a team can view or comment on that team's posts. Each team has a single **access key**. Anyone holding it can upload to the team gallery; only the team owner can regenerate or delete it. See [Team Access Keys](/authentication#team-access-keys). ## Upgrading You can upgrade to PLUS or Ultra anytime from the [pricing page](https://snipp.gg/pricing) or your account settings. Your new limits take effect immediately. Upgrading from PLUS to Ultra replaces your existing subscription rather than adding a second one, and you are charged the difference for the rest of the current period. Ultra includes 1 team with up to 10 members. For a higher team count or seat count, reach out to [support@snipp.gg](mailto:support@snipp.gg). ## What Happens When You Downgrade * Your existing uploads remain accessible regardless of plan changes. * Files that exceed your new plan's size limit cannot be re-uploaded if deleted. * PLUS features such as the PLUS badge are removed at the end of your billing period. # Rate Limits Source: https://docs.snipp.gg/concepts/rate-limits Understand request limits across the API and Relay, and how to handle throttling. ## Overview Rate limits protect Snipp from abuse and keep performance consistent for everyone. Limits apply per user, per endpoint, except where endpoints are marked as sharing a bucket below. ## API Limits | Endpoint | Limit | Window | | ------------------------------------------- | ------------------- | -------------- | | `GET /users/{id}` | No limit | | | `GET /posts/{code}` | 20 requests | Per 30 seconds | | `POST /upload` | 20 requests | Per 30 seconds | | `GET /uploads` | 20 requests | Per 30 seconds | | `PATCH /editUpload` | 20 requests | Per 30 seconds | | `POST /appendUpload` | 10 requests | Per minute | | `DELETE /deleteUpload` | 20 requests | Per 30 seconds | | `POST /report-post` and `POST /report-user` | 3 requests combined | Per minute | | `GET /usage-history` | 30 requests | Per minute | | `GET /analytics` | 30 requests | Per minute | | `GET /gifs` | 60 requests per IP | Per minute | | `POST /convert` | 10 requests | Per minute | `POST /upload` also has an abuse safeguard. Sending more than 10 uploads within one second suspends your account. Suspended accounts lose upload access and must contact support to appeal. Team API keys are throttled with a `429` instead of being suspended. ## Relay Limits The Relay API matches the limits above for shared endpoints, with these additional Relay-only endpoints: | Endpoint | Limit | Window | | --------------------------- | ----------- | ---------------------------------------------------------------------------------- | | `GET /discover` | No limit | | | `GET /blocks` | No limit | | | `POST /block` | 10 requests | Per minute | | `POST /unblock` | 10 requests | Per minute | | `POST /follow` | 30 requests | Per minute | | `POST /unfollow` | 30 requests | Per minute | | `GET /followers` | 30 requests | Per minute, shared with `GET /following` | | `GET /following` | 30 requests | Per minute, shared with `GET /followers` | | `PATCH /profile` | 10 requests | Per minute | | `POST /profile/avatar` | 10 requests | Per minute | | `POST /profile/banner` | 10 requests | Per minute | | `PATCH /theme` | 20 requests | Per minute | | `POST /like` | 30 requests | Per minute, shared with `POST /comment-like` | | `GET /comments` | No limit | | | `POST /comment` | 5 requests | Per minute, shared with `DELETE /comment` | | `DELETE /comment` | 5 requests | Per minute, shared with `POST /comment` | | `POST /comment-like` | 30 requests | Per minute, shared with `POST /like` | | `POST /report-comment` | 3 requests | Per minute, shared with `POST /report-post` and `POST /report-user` | | `GET /notifications` | 60 requests | Per minute, shared with `GET /notifications/unread` and `POST /notifications/read` | | `GET /notifications/unread` | 60 requests | Per minute, shared with `GET /notifications` and `POST /notifications/read` | | `POST /notifications/read` | 60 requests | Per minute, shared with `GET /notifications` and `GET /notifications/unread` | | `GET /notification-prefs` | 20 requests | Per minute, shared with `POST /notification-prefs` | | `POST /notification-prefs` | 20 requests | Per minute, shared with `GET /notification-prefs` | `GET /posts/{code}` and `GET /discover` on Relay automatically count views per post. Views are deduplicated per IP and post: up to 3 counted views per IP per post per hour. See the [Relay Reference](/relay-reference/introduction) for what Relay covers and how to authenticate. ## Rate Limit Headers Rate-limited responses include headers to help you track your usage: | Header | Description | | ----------------------- | ------------------------------------------------------------- | | `X-RateLimit-Limit` | Maximum requests allowed per window | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp (seconds) when the window resets | | `Retry-After` | Seconds to wait before retrying. Sent on `429` responses only | Endpoints with no limit do not include these headers. ## Rate Limit Responses When you exceed the limit, the API returns HTTP status `429`: ```json theme={null} { "error": "Rate limit exceeded. Please try again later." } ``` The rate limit headers are still included on `429` responses so you know when to retry. ## Weekly Usage Limit Responses Separate from per-endpoint rate limits, every account has a [weekly usage limit](/concepts/plans#weekly-usage-limit). Files that would push you over the limit are rejected before they are stored. The exact response shape depends on the endpoint: * `POST /upload` and `POST /appendUpload` return `200` with the rejected file moved to the `failed` array when at least one other file was stored. Each entry includes `error: "Weekly usage limit exceeded."`, plus `quota`, `used`, and `resetsAt`. * When every file in the request is rejected, the status is `400`. A multi-file request returns `error: "All files failed to upload."` alongside the same `failed` array. A single-file request returns the reason at the top level, with `quota`, `used` and `resetsAt` beside it whenever the quota was reserved per file: `POST /appendUpload`, and `POST /upload` on a team key. * A single-file `POST /upload` on a personal key is the exception. Its quota is reserved during the insert, so it returns `400` with `error: "Weekly usage limit exceeded."` and no quota fields. * The Website internal chunked-upload route returns `413` with the same fields at the top level. * The Website internal `/api/v1/album` route moves the rejected file to the `failed` array like `/upload`: `200` when at least one file was stored, `400` when every file failed. The fields sit at the top level only when a single file was sent. Check your remaining usage anytime via `GET /users/@me` (`limits.usage`). ## Handling Rate Limits Read `X-RateLimit-Remaining` from each response. When it approaches `0`, slow down. Check for HTTP status `429` in your API responses. Use the `Retry-After` header on the `429` as the wait time in seconds, or calculate it from `X-RateLimit-Reset`. If requests continue to fail, double the wait time with each retry (for example, 1s, 2s, 4s, 8s). ## Best Practices * **Space out bulk uploads.** When uploading multiple files, add a short delay between each request rather than sending them all at once. * **Cache user data.** Avoid calling `/users/@me` repeatedly. Fetch it once and reuse the result for the duration of your session. * **Batch where possible.** Reduce the total number of API calls by combining logic on your end instead of making multiple requests for related data. * **Monitor your usage.** If you consistently hit rate limits, consider whether your integration can be optimized to make fewer requests. # Discord Bot Overview Source: https://docs.snipp.gg/discord-bot/overview Upload, manage, and check usage on Snipp directly from Discord. ## Overview The Snipp Discord bot mirrors core upload features of the website. Once your Discord account is linked, you can upload files, browse your gallery, and check your weekly usage from any server or DM. ## Linking Your Account Before using the bot, link your Discord account at [snipp.gg](https://snipp.gg/api/auth/discord). The bot looks up your Snipp profile by Discord ID. Without a linked account, every command returns a "No Snipp Account Found" prompt. ## Commands ### `/upload` Opens a modal where you attach one or more files, set privacy (public, unlisted, private), pick album or single mode, and optionally add a title and description. Behavior matches the website: * Single file → one post. * Multiple files in album mode → one album post. * Multiple files in single mode → individual posts per file. File-size and weekly-usage limits apply identically to API uploads. See [Plans](/concepts/plans). ### `/gallery` Shows your 9 most recent uploads with previews and direct share links. Private files include short-lived signed URLs so previews still render in Discord. ### `/usage` Shows your current plan, max file size, and weekly usage with a progress bar. The embed color reflects how close you are to your cap: * Green below 75% * Orange between 75% and 99% * Red at 100% or higher When the weekly cap is hit the embed shows `Weekly usage exceeded • Resets Mon 3:00 PM`. Otherwise it shows the reset time on its own. Example output: ``` Plan: PLUS Max File Size: Unlimited Weekly Uploads: 5.00 GB / 20.00 GB > █████░░░░░░░░░░░░░░░ 25% used Resets Mon 3:00 PM Monthly Priority Minutes: 35 / 300 min > ██░░░░░░░░░░░░░░░░░░ 12% used Resets Aug 22 8:30 PM ``` ## Limits and Restrictions The bot enforces the same per-file and weekly-usage caps as the API. Uploads counted via the bot decrement the same weekly window shown on the website and in `GET /users/@me`. A few bot-specific notes: * Videos are not supported through the bot. Upload videos via the website or API. * PNG, JPEG, WebP and GIF uploads have their metadata stripped, matching the platform default. * Allowed types: `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`. * Discord caps attachments at 10 MB for free users and higher for Nitro. Larger files must be uploaded via the website or API. * The 8-uploads-per-second abuse guard from the API also applies to the bot. ## Suspended Accounts If your account is suspended, the bot blocks `/upload` and surfaces a message linking to the data-deletion page. Contact `support@snipp.gg` to appeal. # Introduction Source: https://docs.snipp.gg/index Welcome to the official Snipp API documentation! Learn how to upload, manage, and share files programmatically. Snipp is a modern file-sharing platform with a developer-friendly API. Upload images and videos, manage your files, and integrate Snipp into your own apps and workflows. Get your API key and make your first upload in minutes. ## What You Can Do Upload images and videos with `POST /upload`. Includes privacy controls. List your recent uploads with `GET /uploads` and remove files with `DELETE /deleteUpload`. Fetch profile data, badges, and public uploads for any user with `GET /users/{id}`. Browse community uploads with `GET /discover` on the Relay API. ## Next Steps Learn how to authenticate requests with your API key. See which formats are supported and their size limits. Understand request limits and best practices. Compare Free, PLUS, and Ultra tiers. # Quickstart Source: https://docs.snipp.gg/quickstart Get your API key and make your first upload in under five minutes. ## Get Started in Three Steps Make your first API call and upload a file to Snipp. ### Step 1: Get Your API Key 1. Sign in to your account. 2. Open **Settings** and navigate to the API section. 3. Copy your API key. Include it in the `api-key` header with every request. Never share your API key or commit it to version control. Store it in an environment variable in production. ### Step 2: Verify Your Setup Test your API key by fetching your own profile: ```bash cURL theme={null} curl -X GET "https://api.snipp.gg/users/@me" \ -H "api-key: YOUR_API_KEY" ``` ```javascript JavaScript theme={null} const res = await fetch("https://api.snipp.gg/users/@me", { headers: { "api-key": "YOUR_API_KEY" }, }); const data = await res.json(); console.log(data.user.username); ``` ```python Python theme={null} import requests res = requests.get( "https://api.snipp.gg/users/@me", headers={"api-key": "YOUR_API_KEY"}, ) print(res.json()["user"]["username"]) ``` A successful response returns your user object: ```json theme={null} { "user": { "id": "123456789012345678", "username": "yourname", "plus": false, "verified": false, "badges": { ... }, "uploads": 0 } } ``` ### Step 3: Upload a File Upload an image or video to your account: ```bash cURL theme={null} curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -H "post-privacy: unlisted" \ -F "file=@/path/to/image.png" ``` ```javascript JavaScript theme={null} const formData = new FormData(); formData.append("file", fileInput.files[0]); const res = await fetch("https://api.snipp.gg/upload", { method: "POST", headers: { "api-key": "YOUR_API_KEY", "post-privacy": "unlisted", }, body: formData, }); const data = await res.json(); console.log(data.url); ``` ```python Python theme={null} import requests with open("image.png", "rb") as f: res = requests.post( "https://api.snipp.gg/upload", headers={ "api-key": "YOUR_API_KEY", "post-privacy": "unlisted", }, files={"file": f}, ) print(res.json()["url"]) ``` These examples use `api.snipp.gg`, which is the right host for production uploads too. It connects straight to the nearest upload server with no per-request size cap. Pin a regional endpoint such as `eu-west-1.api.snipp.gg` only when you want to control which region stores your files. See [Regional endpoints](/api-reference/introduction#regional-endpoints). The response includes a direct URL to your file and a post object with share metadata: ```json theme={null} { "message": "Upload successful!", "url": "https://i.snipp.gg/123456789012345678/abc123.png", "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "unlisted" } } ``` ### Albums Upload multiple files in one request to create an album. Add additional `-F "file=@..."` fields: ```bash theme={null} curl -X POST "https://api.snipp.gg/upload" \ -H "api-key: YOUR_API_KEY" \ -H "post-title: My album" \ -F "file=@photo1.png" \ -F "file=@photo2.png" ``` Albums are the default when sending two or more files. To create separate posts instead, add `-H "post-type: individual"`. ### Privacy Options Control who can see your uploads by setting the `post-privacy` header: | Value | Visibility | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `private` | Only you can view it (default). Response URLs are signed and expire after 24 hours. Re-fetch from `GET /posts/{code}` or `GET /uploads` for a fresh link. | | `unlisted` | Anyone with the share link can view it. | | `public` | Visible to everyone and eligible for the Discover feed. | ## Next Steps Learn how the API key header works and security best practices. Explore all available endpoints and response schemas. See supported formats and size limits per plan. Understand request limits and how to handle throttling. # Get Analytics Source: https://docs.snipp.gg/relay-reference/endpoint/analytics GET /analytics Fetch your own analytics: posts, uploads, data uploaded, comments received, likes received and followers gained over time, plus totals. This is the same data that powers the Analytics page in the Snipp Console, exposed so you can rebuild it in your own tools. ## Behavior * Authenticated with your `relay-key`. Returns analytics for the key's owner only. * Requires an active PLUS subscription. Without it the endpoint returns `403`. * `range` selects the window for `series` and `deltas`. Valid values are 7, 30, 90 and 365. It defaults to 30 if omitted or invalid. `buckets` are always today, last 7 days and last 30 days regardless of `range`. * `series` arrays have one entry per day across the window, oldest first, including days with no activity (value `0`). * `totals.posts` and `totals.views` are lifetime; `totals.views` is the sum of views across all your posts. `totals.comments`, `totals.likes` and `totals.bytes` cover the fetch window only, which is twice the selected `range` with a floor of 30 days. * `deltas.change` is the fractional change against the preceding window of equal length. It is `null` when the preceding window was zero and the current one is not, and `0` when both are zero. * `payout` is the creator payout estimate for the current biweekly payout period, for monetized creators only. `payout.eligible` is `false` for non-monetized accounts; when `true`, `payout.payout` is the estimated dollar share and `payout.series` is the daily referral score for the period. Earnings come from referrals, not from your own views or likes. The estimate is live and moves as referrals land through the period. * All dates are UTC. ## Examples Fetch your analytics for the last 90 days: ```bash theme={null} curl -X GET "https://relay.snipp.gg/analytics?range=90" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "analytics": { "range": 30, "buckets": { "posts": { "today": 2, "week": 11, "month": 47 }, "uploads": { "today": 3, "week": 14, "month": 63 }, "bytes": { "today": 1048576, "week": 8388608, "month": 27918336 }, "comments": { "today": 0, "week": 4, "month": 18 }, "likes": { "today": 1, "week": 9, "month": 38 }, "followers": { "today": 1, "week": 6, "month": 21 } }, "totals": { "posts": 666, "views": 1695, "comments": 18, "likes": 412, "bytes": 27918336 }, "series": { "posts": [{ "day": "2026-05-06", "value": 1 }, { "day": "2026-05-07", "value": 0 }], "uploads": [{ "day": "2026-05-06", "value": 2 }, { "day": "2026-05-07", "value": 0 }], "bytes": [{ "day": "2026-05-06", "value": 524288 }, { "day": "2026-05-07", "value": 0 }], "comments": [{ "day": "2026-05-06", "value": 1 }, { "day": "2026-05-07", "value": 0 }], "likes": [{ "day": "2026-05-06", "value": 3 }, { "day": "2026-05-07", "value": 1 }], "followers": [{ "day": "2026-05-06", "value": 2 }, { "day": "2026-05-07", "value": 0 }], "views": [{ "day": "2026-05-06", "value": 24 }, { "day": "2026-05-07", "value": 11 }] }, "deltas": { "posts": { "current": 47, "previous": 41, "change": 0.1463 }, "uploads": { "current": 63, "previous": 55, "change": 0.1454 }, "bytes": { "current": 27918336, "previous": 25000000, "change": 0.1167 }, "comments": { "current": 18, "previous": 22, "change": -0.1818 }, "likes": { "current": 38, "previous": 31, "change": 0.2258 }, "followers": { "current": 21, "previous": 17, "change": 0.2353 }, "views": { "current": 1695, "previous": 1402, "change": 0.2090 } } }, "payout": { "eligible": true, "payout": 24.47, "series": [{ "day": "2026-06-01", "value": 0 }, { "day": "2026-06-02", "value": 15 }] } } ``` # Add to Album Source: https://docs.snipp.gg/relay-reference/endpoint/append POST /appendUpload Add one or more files to an existing album post. Use this to grow a post's file list in place. The post's share code, privacy, title, and description stay the same. ## Headers | Header | Required | Description | | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `relay-key` | Yes | Your Relay key | | `post-code` | Yes | The share code of the post to append to. `code` works as an alias. | | `include-metadata` | No | `true` to keep file metadata. Omitted or `false` strips it (default). | | `priority` | No | `false` to opt appended video files out of priority (adaptive) streaming. Omitted or `true` keeps it on (default), subject to your remaining priority minutes. | ## Behavior * You must own the post. * The post cannot be moderated. Moderated posts reject modifications. * Albums cap at **50 files total**. Requests that would exceed the cap are rejected. * Newly added files inherit the post's existing privacy. If the post is **private**, returned `url`s are signed with a 24-hour expiry. * Video files (`.mp4`, `.mov`, `.mkv`) are remuxed to MP4 with `+faststart`. See [File Types](/concepts/file-types) for details. * Metadata is stripped by default: PNG, JPG, WebP and GIF are stripped structurally at any size, so the pixels are untouched, and videos lose their container metadata during the MP4 remux. Send `include-metadata: true` to keep it. * Appended videos use priority (adaptive) streaming by default, spending [priority minutes](/concepts/plans). Send `priority: false` to add them in standard quality instead. The post's `priority` field is `true` when any file in the album is on adaptive streaming. * Each new file is auto-moderated. If any file (or the post's title or description) is flagged, the entire post, including existing files, is quarantined and marked `moderated`. ## Examples Add two images to an existing album: ```bash theme={null} curl -X POST "https://relay.snipp.gg/appendUpload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "post-code: AbCd1234" \ -F "file=@photo4.png" \ -F "file=@photo5.png" ``` Convert a single-file post into an album by appending a second file: ```bash theme={null} curl -X POST "https://relay.snipp.gg/appendUpload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "post-code: AbCd1234" \ -F "file=@second.png" ``` ## Responses ```json theme={null} { "message": "Files added to album successfully.", "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "fileCount": 5, "priority": false }, "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1920, "height": 1080 } } ] } ``` If some files fail, a `failed` array is included alongside `files`: ```json theme={null} { "failed": [ { "index": 4, "error": "Failed to process file.", "status": "failed" } ] } ``` # Upload Avatar Source: https://docs.snipp.gg/relay-reference/endpoint/avatar POST /profile/avatar Upload a new avatar via multipart form upload. ## Behavior * Accepts `.png`, `.jpg`, `.webp`, `.gif` up to **30 MB**. * Image is square-cropped to the shorter side using a centered crop. * Stored under `avatars/{userId}/` on the Snipp CDN. * Sets the field on your profile automatically. No follow-up [`/profile`](/relay-reference/endpoint/profile) call required. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/profile/avatar" \ -H "relay-key: YOUR_RELAY_KEY" \ -F "file=@avatar.png" ``` ## Responses ```json theme={null} { "url": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "type": "avatar" } ``` # Upload Banner Source: https://docs.snipp.gg/relay-reference/endpoint/banner POST /profile/banner Upload a new banner via multipart form upload. ## Behavior * Accepts `.png`, `.jpg`, `.webp`, `.gif` up to **30 MB**. * Banner keeps its original aspect ratio (no crop). * Stored under `banners/{userId}/` on the Snipp CDN. * Sets the field on your profile automatically. No follow-up [`/profile`](/relay-reference/endpoint/profile) call required. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/profile/banner" \ -H "relay-key: YOUR_RELAY_KEY" \ -F "file=@banner.png" ``` ## Responses ```json theme={null} { "url": "https://i.snipp.gg/banners/123456789012345678/banner.png", "type": "banner" } ``` # Block User Source: https://docs.snipp.gg/relay-reference/endpoint/block POST /block Block a user by ID. ## Behavior * Blocking is bidirectional in effect. Blocked users cannot see your posts or profile, and you cannot see theirs. * Blocking an already-blocked user succeeds without error. * You cannot block yourself. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/block" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetId": "123456789012345678"}' ``` ## Responses ```json theme={null} { "blocked": true } ``` Blocking an already-blocked user succeeds and includes a `message`: ```json theme={null} { "blocked": true, "message": "User is already blocked." } ``` # List Blocks Source: https://docs.snipp.gg/relay-reference/endpoint/blocks GET /blocks Return the list of users you have blocked. ## Behavior * Returns each block as a `userId` (the blocked user's snowflake ID) and a `created` timestamp. * Use [`GET /users/{id}`](/relay-reference/endpoint/users) on each `userId` to fetch usernames and avatars. * Users who have blocked you do not appear here. This endpoint only lists blocks you initiated. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/blocks" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "blocks": [ { "userId": "123456789012345678", "created": "2026-05-03T12:00:00Z" } ] } ``` # Create Comment Source: https://docs.snipp.gg/relay-reference/endpoint/comment-create POST /comment Add a comment to a post. ## Behavior * Comments are limited to **200 characters**. * A post holds up to **500 comments**. * Commenting is blocked on moderated posts and on private posts you do not own. * Any `@username` in the comment sends that user a mention notification. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/comment" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"shareCode": "AbCd1234", "text": "Nice shot!"}' ``` ## Responses Returns the created comment plus the post's full updated comment list. ```json theme={null} { "comment": { "id": "00000000-0000-4000-8000-000000000000", "body": "Nice shot!", "userId": "123456789012345678", "createdAt": "2026-05-03T12:00:00Z", "username": "playerone", "nickname": "Player One", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "isVerified": false, "plus": true, "likes": 0, "liked": false }, "comments": [ { "id": "00000000-0000-4000-8000-000000000000", "body": "Nice shot!", "userId": "123456789012345678", "createdAt": "2026-05-03T12:00:00Z", "username": "playerone", "nickname": "Player One", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "isVerified": false, "plus": true, "likes": 0, "liked": false } ] } ``` # Delete Comment Source: https://docs.snipp.gg/relay-reference/endpoint/comment-delete DELETE /comment Delete a comment by ID. ## Behavior * Only the comment author or the post owner can delete a comment. * Returns the post's updated comment list. ## Examples ```bash theme={null} curl -X DELETE "https://relay.snipp.gg/comment" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"shareCode": "AbCd1234", "commentId": "00000000-0000-4000-8000-000000000000"}' ``` ## Responses Returns the post's updated comment list with the deleted comment removed. ```json theme={null} { "comments": [ { "id": "11111111-1111-4111-8111-111111111111", "body": "Still here", "userId": "987654321098765432", "createdAt": "2026-05-03T12:05:00Z", "username": "playertwo", "nickname": "Player Two", "avatar": "https://i.snipp.gg/avatars/987654321098765432/avatar.png", "isVerified": false, "plus": false } ] } ``` # Like Comment Source: https://docs.snipp.gg/relay-reference/endpoint/comment-like POST /comment-like Like a comment. Calling it twice removes the like. ## Behavior * Likes are blocked on comments belonging to moderated posts, and on private posts you do not own. * You cannot like a comment whose author has blocked you. * The comment author gets a notification, unless they have turned off like notifications. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/comment-like" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"commentId": "00000000-0000-4000-8000-000000000000"}' ``` ## Responses `liked` reflects the state after the call. Calling the endpoint again toggles it back. ```json theme={null} { "liked": true, "likes": 4 } ``` # List Comments Source: https://docs.snipp.gg/relay-reference/endpoint/comments GET /comments List comments on a post. Pass `shareCode` as a query parameter or `code` as a header. ## Behavior * Returns each comment with the author's username, nickname, avatar, verified flag, and PLUS flag. * `likes` is the total like count on the comment, and `liked` is whether you have liked it. Use [Like Comment](/relay-reference/endpoint/comment-like) to toggle. * Comments from suspended users and from users you have blocked are filtered out. * Private posts and moderated posts are visible only to the owner. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/comments?shareCode=AbCd1234" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "comments": [ { "id": "00000000-0000-4000-8000-000000000000", "body": "Nice shot!", "userId": "123456789012345678", "createdAt": "2026-05-03T12:00:00Z", "username": "playerone", "nickname": "Player One", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "isVerified": false, "plus": true, "likes": 3, "liked": false } ] } ``` # Convert a File Source: https://docs.snipp.gg/relay-reference/endpoint/convert POST /convert Convert one image or video to another format and get the converted file back in the response. Nothing is stored, no post is created, and no upload quota is consumed. ## Headers | Header | Required | Description | | ---------------- | -------- | ----------------------------------------------------- | | `relay-key` | Yes | Your Relay key | | `convert-target` | Yes | `png`, `jpeg`, `webp`, `avif`, `gif`, `mp4`, or `mp3` | ## Behavior * Send the file as `multipart/form-data`. Only the first file in the request is read. * The response body is the converted file itself, sent as an attachment, not JSON. Write it straight to disk. * The source format is detected from the file's own bytes, not its name or the declared content type. * **What can become what:** PNG, JPG and WebP convert to png, jpeg, webp, avif or gif. GIF converts to png, jpeg, webp, avif or mp4. MP4, MOV and MKV convert to mp4, gif or mp3. * **Video to GIF requires Snipp+** and uses the first 10 seconds only, at 15fps and 480px wide. * **MP4 output** is remuxed with `+faststart` when the source is already H.264, and transcoded otherwise. * **Metadata is always stripped.** Images are re-encoded without it and videos are written with `-map_metadata -1`. JPG orientation is applied to the pixels first, so photos stay the right way up. * Files are capped at 50 MB on the free plan. Snipp+ and Ultra have no per-file cap, the same as [`/upload`](/relay-reference/endpoint/upload). Conversion runs inside the request, so a large video can take a while to come back. * Rate limited to 10 conversions per minute. Converting does not count toward your weekly upload limit. * To host the result, pipe it into [`POST /upload`](/relay-reference/endpoint/upload). Note that avif and mp3 are not accepted upload types, so those two are download-only. ## Examples Convert a screenshot to WebP: ```bash theme={null} curl -X POST "https://relay.snipp.gg/convert" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "convert-target: webp" \ -F "file=@screenshot.png" \ -o screenshot.webp ``` Pull the audio out of a clip: ```bash theme={null} curl -X POST "https://relay.snipp.gg/convert" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "convert-target: mp3" \ -F "file=@clip.mp4" \ -o clip.mp3 ``` Convert, then host the result: ```bash theme={null} curl -X POST "https://relay.snipp.gg/convert" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "convert-target: mp4" \ -F "file=@loop.gif" \ -o loop.mp4 curl -X POST "https://relay.snipp.gg/upload" \ -H "relay-key: YOUR_RELAY_KEY" \ -F "file=@loop.mp4" ``` ## Responses On success the body is the converted file and the headers name it: ``` HTTP/1.1 200 OK Content-Type: image/webp Content-Disposition: attachment; filename="screenshot.webp" Content-Length: 184320 ``` On failure the body is JSON: ```json theme={null} { "error": "That file cannot be converted to this format." } ``` # Delete Upload Source: https://docs.snipp.gg/relay-reference/endpoint/delete DELETE /deleteUpload Delete a file from your account by filename. ## Behavior * Pass the file's name in the `file` header. The filename is 32 hex characters plus an extension (for example, `a1b2c3d4e5f6789012345678abcdef01.png`). * Get the filename from the `files[].fileName` field on [`GET /posts/{code}`](/relay-reference/endpoint/post) or [`GET /uploads`](/relay-reference/endpoint/uploads). * On a single-file post, deleting the file deletes the entire post. * On an album, deleting a file removes just that file. Deleting the last remaining file deletes the album post. * You can only delete your own uploads. ## Examples ```bash theme={null} curl -X DELETE "https://relay.snipp.gg/deleteUpload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "file: a1b2c3d4e5f6789012345678abcdef01.png" ``` ## Responses ```json theme={null} { "message": "File deleted successfully." } ``` # Discover Feed Source: https://docs.snipp.gg/relay-reference/endpoint/discover GET /discover Browse public uploads from the community. Returns up to 50 recent items in randomized order. ## Behavior * Only `public` posts are returned. `unlisted` and `private` posts never appear in Discover. * Moderated posts are excluded. * Blocked users are filtered both ways: posts from users you have blocked, and posts from users who have blocked you, are excluded. * Hits to this endpoint count a view toward each returned post. Views are deduplicated per IP and post: up to 3 counted views per IP per post per hour. * Files that no longer exist on the CDN are skipped silently. * `likeCount` is the total like count on the post, and `liked` is whether you have liked it. Use [Like Post](/relay-reference/endpoint/like) to toggle. * `thumbnailUrl` is only present when the post has a thumbnail. * `priority` is `true` when the post's video is served over priority delivery, and `false` for standard delivery. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/discover" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "uploads": [ { "url": "https://i.snipp.gg/123456789012345678/abc123.png", "thumbnailUrl": "https://i.snipp.gg/123456789012345678/thumb/16483ec5681c5fc2b5cc08dd49b7dbae.jpg", "code": "AbCd1234", "title": "Nice shot", "description": null, "created": "2026-05-03T12:00:00Z", "views": 128, "likeCount": 42, "liked": false, "commentCount": 3, "priority": false, "author": "Player One", "authorId": "123456789012345678", "authorUsername": "playerone", "authorAvatar": "https://cdn.snipp.gg/avatars/123456789012345678/avatar.png", "authorPlus": true, "isVerified": false } ] } ``` Each item is the primary file of its post. To fetch the full album, call [`GET /posts/{code}`](/relay-reference/endpoint/post) with the `code` field. # Edit Upload Source: https://docs.snipp.gg/relay-reference/endpoint/edit PATCH /editUpload Update an existing upload's title, description, privacy level, or priority streaming. ## Behavior * You must own the upload to edit it, or belong to the owning team with a role other than viewer. * At least one of `title`, `description`, `post-privacy`, or `priority` must be provided. * Send `title` or `description` as multipart form fields. This is required for non-ASCII values such as emoji or accented characters, which cannot be carried in HTTP headers. The `title` and `description` headers still work for plain ASCII values but are discouraged. * Send an empty string for `title` or `description` to clear the field. * `title` is truncated to 30 characters, `description` to 200 characters. * A newly added `@username` in the description sends that user a mention notification. Mentions already present before the edit are not re-notified. * A restricted post cannot be made public. Setting `post-privacy` to `public` on a restricted post returns `403`. You can still switch it between `private` and `unlisted`. * `priority` is `true` or `false` and moves the post's videos on or off priority (adaptive) streaming. Turning it on with no priority minutes left returns `403`. Any other value returns `400`. * The response always carries `priority`, reflecting where the post's videos ended up. ## Examples Update a post's title and privacy: ```bash theme={null} curl -X PATCH "https://relay.snipp.gg/editUpload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "code: AbCd1234" \ -H "post-privacy: public" \ -F "title=New title 🎉" ``` Clear a post's description: ```bash theme={null} curl -X PATCH "https://relay.snipp.gg/editUpload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "code: AbCd1234" \ -F "description=" ``` ## Responses ```json theme={null} { "message": "Upload updated successfully.", "post": { "code": "AbCd1234", "title": "New title", "description": null, "postPrivacy": "public", "priority": false } } ``` # Follow User Source: https://docs.snipp.gg/relay-reference/endpoint/follow POST /follow Follow a user by ID. ## Behavior * Following an already-followed user succeeds without error. * You cannot follow yourself. * You cannot follow a user who has blocked you, or whom you have blocked. * Suspended users cannot be followed. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/follow" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetId": "123456789012345678"}' ``` ## Responses ```json theme={null} { "following": true } ``` # List Followers Source: https://docs.snipp.gg/relay-reference/endpoint/followers GET /followers Return the list of users following a given profile. ## Behavior * Pass `id` as a query parameter to list followers of that user. Omit it to list your own followers. * Capped at 200 results, newest first. * Suspended users are excluded. * Each entry includes a `youFollow` flag indicating whether you follow that user back, and `isYou` for your own account. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/followers?id=123456789012345678" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "users": [ { "id": "987654321098765432", "username": "alice", "nickname": "Alice", "avatar": "https://i.snipp.gg/avatars/987654321098765432/abc.webp", "verified": false, "youFollow": true, "isYou": false } ] } ``` # List Following Source: https://docs.snipp.gg/relay-reference/endpoint/following GET /following Return the list of users a given profile is following. ## Behavior * Pass `id` as a query parameter to list who that user is following. Omit it to list who you are following. * Capped at 200 results, newest first. * Suspended users are excluded. * Each entry includes a `youFollow` flag indicating whether you also follow that user, and `isYou` for your own account. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/following?id=123456789012345678" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "users": [ { "id": "987654321098765432", "username": "alice", "nickname": "Alice", "avatar": "https://i.snipp.gg/avatars/987654321098765432/abc.webp", "verified": false, "youFollow": true, "isYou": false } ] } ``` # List Public GIFs Source: https://docs.snipp.gg/relay-reference/endpoint/gifs GET /gifs Browse public GIFs across Snipp, newest first, like a public GIF feed. Defaults to 30. Pass `?limit=N` (max 50) and `?offset=N` to page. Pass `?search=kitty cat` to match word prefixes in the title or description. ## Behavior * No authentication required. Rate limited per IP. * Only posts whose first file is a `.gif` are returned. * Returns public posts only. Unlisted, private, draft, moderated, and team posts are excluded, as are posts from suspended accounts. * `?search` matches word prefixes (case-insensitive) in the title or description. `kitt` matches "kitty" and "kitten" but not "skittle", since it must match the start of a word. * Multiple words are combined with AND, order independent. `kit cat` matches a post whose title or description has a word starting with "kit" and a word starting with "cat", anywhere. Up to 6 words are used. * `thumbnailUrl` is only present when the post has a thumbnail. * `title` and `description` are `null` when none is set. * `hasMore` is `true` when another page is likely available. Page with `offset`. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/gifs?limit=50&offset=0" ``` ```bash theme={null} curl -X GET "https://relay.snipp.gg/gifs?search=kitty" ``` ## Responses ```json theme={null} { "gifs": [ { "code": "AbCd1234", "url": "https://i.snipp.gg/364836696149458944/16483ec5681c5fc2b5cc08dd49b7dbae.gif", "thumbnailUrl": "https://i.snipp.gg/364836696149458944/thumb/16483ec5681c5fc2b5cc08dd49b7dbae.jpg", "title": "Nice loop", "description": null, "created": "2026-03-27T11:42:18.000Z", "views": 128 } ], "hasMore": true, "offset": 0, "limit": 50 } ``` # Like Post Source: https://docs.snipp.gg/relay-reference/endpoint/like POST /like Like a post. Calling it twice removes the like. ## Behavior * Likes are blocked on moderated posts and on private posts you do not own. * Team posts cannot be liked. Members get a `403`; everyone else gets a `404`. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/like" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"shareCode": "AbCd1234"}' ``` ## Responses `liked` reflects the state after the call. Calling the endpoint again toggles it back. ```json theme={null} { "liked": true, "likeCount": 42 } ``` # Notification Preferences Source: https://docs.snipp.gg/relay-reference/endpoint/notification-prefs POST /notification-prefs Read and update which notifications and emails you receive. `GET` returns the current values, `POST` updates them. ## Behavior * Send only the keys you want to change. Anything you leave out keeps its current value. * `notifications.likes` covers both post likes and comment likes. * Some notifications cannot be turned off and are not listed here: post removals, post restrictions, and account changes. The same applies to security and transactional emails such as login links, email changes, moderation notices and suspensions. * `emails.payouts` (creator payout and referral emails) is on by default. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/notification-prefs" \ -H "relay-key: YOUR_RELAY_KEY" ``` ```bash theme={null} curl -X POST "https://relay.snipp.gg/notification-prefs" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"notifications": {"likes": false}, "emails": {"payouts": false}}' ``` ## Responses Both methods return the full set of preferences after the call. ```json theme={null} { "notifications": { "follows": true, "likes": false, "comments": true, "payouts": true }, "emails": { "payouts": true } } ``` # List Notifications Source: https://docs.snipp.gg/relay-reference/endpoint/notifications GET /notifications List your notifications, newest first. ## Behavior * Likes, comment likes and follows are **aggregated**. One entry can stand for several events, so read `actorCount` and `postCount` rather than assuming one entry means one person. * Likes on the same post group together: `actorCount` is how many people liked it. * A single person liking several of your posts groups together: `postCount` is how many posts. * Comment likes group by comment. Comments never group, since each carries its own text. * `actors` is capped at 10 even when `actorCount` is higher. It is enough to render an avatar row. * Grouping is applied per page, so an aggregated entry only covers events within the same page of results. * `mention` notifications fire when someone @-mentions you in a post description or a comment. They are always delivered and cannot be turned off. `body` holds the text the mention appeared in. * `body` carries the comment excerpt for comment, comment-like, and mention notifications, and the payout amount for payout notifications. Do not render it as an excerpt without checking `type` first. * `postText` is the related post's text (its title, falling back to its description). Use it as the excerpt for post likes, the way `body` is used for comments. * `postThumb` is null for videos with no thumbnail and for posts that have been moderated. ## Pagination Pass the `createdAt` of the last entry you received as `before` to fetch the next page. Keep going while `hasMore` is true. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/notifications?tab=social" \ -H "relay-key: YOUR_RELAY_KEY" ``` ```bash theme={null} curl -X GET "https://relay.snipp.gg/notifications?before=2026-07-13T10:00:00Z" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "notifications": [ { "id": "00000000-0000-4000-8000-000000000000", "type": "like", "postCode": "AbCd1234", "body": null, "read": false, "createdAt": "2026-07-13T10:00:00Z", "actors": [ { "username": "playerone", "nickname": "Player One", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "verified": false } ], "actorCount": 3, "postCount": 1, "postThumb": "https://i.snipp.gg/123456789012345678/thumb/clip.jpg", "postText": "clutch 1v3 on dust2" } ], "hasMore": false } ``` The entry above means "Player One and 2 others liked your post". # Mark Notifications Read Source: https://docs.snipp.gg/relay-reference/endpoint/notifications-read POST /notifications/read Mark every unread notification as read. ## Behavior * Marks all unread notifications at once. There is no way to mark a single one. * Read notifications stay in the list, they just no longer count toward the unread badge. There is no endpoint to delete them. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/notifications/read" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "read": true } ``` # Unread Count Source: https://docs.snipp.gg/relay-reference/endpoint/notifications-unread GET /notifications/unread Number of unread notifications, for a badge. ## Behavior * Counts raw notifications, not the aggregated groups returned by [List Notifications](/relay-reference/endpoint/notifications). Three people liking the same post count as three unread, but render as one entry. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/notifications/unread" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "unread": 4 } ``` # Get Post Source: https://docs.snipp.gg/relay-reference/endpoint/post GET /posts/{code} Fetch a post by its share code. ## Behavior * Albums (posts with more than one file) include `urls` and `isAlbum: true`. * Video posts include `thumbnailUrl`, a generated still frame. For private posts it is a signed URL with the same 24-hour expiry as the file URL. * The `author` object describes the post's uploader. `author` is `null` if the user has been deleted. * Use `fileName` from the `files` array when calling [`/deleteUpload`](/relay-reference/endpoint/delete) to address a specific file. On albums, `/deleteUpload` removes just that file; on single-file posts it deletes the post. * Hits to this endpoint count a view toward the post. Views are deduplicated per IP and post: up to 3 counted views per IP per post per hour. Views are only counted on public, non-moderated posts. * Private posts are only visible to the owner. Requests for another user's private post return `404`. * Team posts are only visible to members of that team; everyone else gets a `404`. They omit `likeCount` and `liked`, since team posts cannot be liked. `commentCount` is still returned. * URLs for private posts are returned as signed URLs with a 24-hour expiry. After that, request the endpoint again for a fresh signed URL. * Moderated posts are only fully returned to their owner, with `moderated: true` on the post. Requests by anyone else return `403` with `moderated: true`. * Restricted posts carry `restricted: true` on the post when returned to their owner. A restricted post is forced to `unlisted` and cannot be set back to public; see [Edit Upload](/relay-reference/endpoint/edit). * `priority` is `true` when the post's video is served over priority delivery, and `false` for standard delivery. ## Examples Fetch a post by code: ```bash theme={null} curl -X GET "https://relay.snipp.gg/posts/AbCd1234" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses Single-file post: ```json theme={null} { "post": { "code": "AbCd1234", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "width": 1920, "height": 1080, "mime_type": "image/png", "size": 2487312, "size_formatted": "2.37 MB" } ], "title": "Nice shot", "description": null, "postPrivacy": "public", "created": "2026-05-03T12:00:00Z", "views": 128, "likeCount": 42, "liked": false, "commentCount": 3, "author": { "id": "123456789012345678", "username": "playerone", "nickname": "Player One", "avatar": "https://cdn.snipp.gg/avatars/123456789012345678/avatar.png", "verified": false, "plus": true }, "priority": false, "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } } } } ``` Album post (includes `urls` and `isAlbum`): ```json theme={null} { "post": { "code": "AbCd1234", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "urls": [ "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png" ], "isAlbum": true, "files": [ { "index": 0, "fileName": "a1b2c3d4e5f6789012345678abcdef01.png", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "width": 1920, "height": 1080, "mime_type": "image/png", "size": 2487312, "size_formatted": "2.37 MB" }, { "index": 1, "fileName": "b2c3d4e5f6789012345678abcdef0102.png", "url": "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png", "width": 1280, "height": 720, "mime_type": "image/png", "size": 1843210, "size_formatted": "1.76 MB" } ], "title": "Match highlights", "description": null, "postPrivacy": "public", "created": "2026-05-03T12:00:00Z", "views": 128, "likeCount": 42, "liked": false, "commentCount": 3, "author": { "id": "123456789012345678", "username": "playerone", "nickname": "Player One", "avatar": "https://cdn.snipp.gg/avatars/123456789012345678/avatar.png", "verified": false, "plus": true }, "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } } } } ``` # Update Profile Source: https://docs.snipp.gg/relay-reference/endpoint/profile PATCH /profile Update your profile fields. Send only the fields you want to change. ## Behavior * `bio` is capped at 200 characters. * `nickname` is capped at 15 characters. * `username` must be 4-15 characters, letters, numbers, and underscores only. * `email` must be a valid email and at most 50 characters. It cannot be removed, so sending `null` or an empty string is rejected. A new address does not take effect right away: a confirmation link is sent to it and the address only changes once that link is opened. * `socials` is an object keyed by `roblox`, `github`, `spotify`, `youtube`, `tiktok`, `X`, `bluesky`, `discord`. Each value must be a URL on the platform's domain. * `avatar` and `banner` only accept URLs that point to your own `avatars/{userId}/` or `banners/{userId}/` prefix on the Snipp CDN. Use [`/profile/avatar`](/relay-reference/endpoint/avatar) or [`/profile/banner`](/relay-reference/endpoint/banner) to upload first. * `customEmbed` is PLUS-only. Object fields: * `title` (string, max 60): embed title template. Supports variables: `%{post_title}%`, `%{post_description}%`, `%{username}%`, `%{nickname}%`. * `description` (string, max 200): embed description template. Same variables as `title`. * `themeColor` (hex string, e.g. `#F0B100`): embed accent color. * `smallText` (string, max 50): site name / small text shown in embed. ## Examples ```bash theme={null} curl -X PATCH "https://relay.snipp.gg/profile" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"bio": "Building things at Snipp", "nickname": "Snipp"}' ``` ## Responses Only the fields you sent are echoed back alongside the confirmation message. `email` is never echoed back; `emailChangePending` tells you whether a confirmation link was sent. ```json theme={null} { "message": "Profile updated successfully!", "emailChangePending": false, "bio": "Building things at Snipp", "nickname": "Snipp" } ``` # Get Region Source: https://docs.snipp.gg/relay-reference/endpoint/region GET /region Returns which region served the request. Useful for confirming you're hitting the regional endpoint you intended, or for debugging routing through `relay.snipp.gg`. ## Behavior * No authentication required. * `region` is the short region code (for example, `eu-west-1` or `us-west-1`). Returns `unknown` if the server has no region configured. * `country` is the country name mapped from the region prefix (for example, `United States`). `null` if no country mapping is available. * `label` is a human-readable form (for example, `United States (us-west-1)`). Falls back to the raw region code if no country mapping is available. ## Examples Confirm US West is responding: ```bash theme={null} curl -X GET "https://us-west-1.relay.snipp.gg/region" ``` Check which region `relay.snipp.gg` routed you to: ```bash theme={null} curl -X GET "https://relay.snipp.gg/region" ``` ## Responses ```json theme={null} { "region": "us-west-1", "country": "United States", "label": "United States (us-west-1)" } ``` # Report a Comment Source: https://docs.snipp.gg/relay-reference/endpoint/report-comment POST /report-comment Report a comment for violating community guidelines. Reports are reviewed by the moderation team. ## Endpoint ### Report a Comment ``` POST /report-comment ``` Submit a report for a comment using its ID. An optional reason can be provided to help moderators review the report. **Request body:** ```json theme={null} { "commentId": "00000000-0000-4000-8000-000000000000", "reason": "This comment is harassment" } ``` | Field | Type | Required | Description | | ----------- | ------ | -------- | ------------------------------------------ | | `commentId` | string | Yes | The ID of the comment to report | | `reason` | string | No | Reason for the report (max 200 characters) | **Response:** ```json theme={null} { "success": true } ``` ## Behavior * Reports are blocked on comments belonging to moderated posts, on private or draft posts you do not own, and on team posts you are not a member of. * You cannot report a comment whose author has blocked you. * You cannot report your own comment. ## Errors | Code | Error | When | | ----- | -------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `400` | Invalid comment. | `commentId` is empty, missing, or not a valid ID | | `400` | Reason cannot exceed 200 characters. | `reason` exceeds 200 characters | | `403` | You cannot report your own comment. | The comment is yours | | `403` | Comments are disabled on moderated posts. | The post has been moderated | | `403` | You cannot report this comment. | The comment author has blocked you | | `404` | Comment not found. | The comment does not exist | | `404` | Post not found. | The post does not exist, is private or a draft you do not own, or belongs to a team you are not in | | `429` | Rate limit exceeded. Please try again later. | Too many requests | ## Examples Report a comment: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-comment" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"commentId": "00000000-0000-4000-8000-000000000000", "reason": "Harassment"}' ``` Report a comment without a reason: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-comment" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"commentId": "00000000-0000-4000-8000-000000000000"}' ``` ## Responses ```json theme={null} { "success": true } ``` # Report a Post Source: https://docs.snipp.gg/relay-reference/endpoint/report-post POST /report-post Report a post for violating community guidelines. Reports are reviewed by the moderation team. ## Endpoint ### Report a Post ``` POST /report-post ``` Submit a report for a post using its share code. An optional reason can be provided to help moderators review the report. **Request body:** ```json theme={null} { "code": "AbCd1234", "reason": "This post contains inappropriate content" } ``` | Field | Type | Required | Description | | -------- | ------ | -------- | ------------------------------------------ | | `code` | string | Yes | The share code of the post to report | | `reason` | string | No | Reason for the report (max 200 characters) | **Response:** ```json theme={null} { "success": true } ``` ## Errors | Code | Error | When | | ----- | -------------------------------------------- | ------------------------------------------------ | | `400` | Missing or invalid post code. | `code` is empty or missing | | `400` | Reason cannot exceed 200 characters. | `reason` exceeds 200 characters | | `403` | This post has already been moderated. | The post has already been actioned by moderators | | `403` | You cannot report your own post. | `code` belongs to a post you own | | `403` | You cannot report this post. | The post author has blocked you | | `404` | Post not found. | Post does not exist, or is private or a draft | | `429` | Rate limit exceeded. Please try again later. | Too many requests | ## Examples Report a post: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-post" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"code": "AbCd1234", "reason": "Inappropriate content"}' ``` Report a post without a reason: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-post" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"code": "AbCd1234"}' ``` ## Responses ```json theme={null} { "success": true } ``` # Report a User Source: https://docs.snipp.gg/relay-reference/endpoint/report-user POST /report-user Report a user for violating community guidelines. Reports are reviewed by the moderation team. ## Endpoint ### Report a User ``` POST /report-user ``` Submit a report for a user using their ID. An optional reason can be provided to help moderators review the report. **Request body:** ```json theme={null} { "targetUserId": "123456789012345678", "reason": "This user is impersonating someone else" } ``` | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------ | | `targetUserId` | string | Yes | The ID of the user to report | | `reason` | string | No | Reason for the report (max 200 characters) | **Response:** ```json theme={null} { "success": true } ``` ## Errors | Code | Error | When | | ----- | -------------------------------------------- | --------------------------------------------------- | | `400` | Missing or invalid user. | `targetUserId` is empty, missing, or not a valid ID | | `400` | Reason cannot exceed 200 characters. | `reason` exceeds 200 characters | | `403` | This user has already been suspended. | The user has already been actioned by moderators | | `403` | You cannot report yourself. | `targetUserId` is your own ID | | `403` | You cannot report this user. | The reported user has blocked you | | `404` | User not found. | The user does not exist | | `429` | Rate limit exceeded. Please try again later. | Too many requests | ## Examples Report a user: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-user" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetUserId": "123456789012345678", "reason": "Impersonation"}' ``` Report a user without a reason: ```bash theme={null} curl -X POST "https://relay.snipp.gg/report-user" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetUserId": "123456789012345678"}' ``` ## Responses ```json theme={null} { "success": true } ``` # Update Theme Source: https://docs.snipp.gg/relay-reference/endpoint/theme PATCH /theme Set your account color theme. The theme is stored on your account and returned as `theme` on your [`/users/@me`](/relay-reference/endpoint/users) profile. ## Behavior * This endpoint is PLUS-only. Non-PLUS accounts receive a `403`. * `theme` must be one of the supported theme keys: `red`, `orange`, `amber`, `yellow`, `pastel_yellow`, `pastel_orange`, `lime`, `light_green`, `slime`, `green`, `emerald`, `teal`, `ice`, `cyan`, `sky`, `pastel_blue`, `blue`, `midnight_blue`, `indigo`, `light_violet`, `violet`, `purple`, `magneta`, `fuchsia`, `pastel_purple`, `light_pink`, `pink`, `rose`, `dark_rose`, `dynamic`. * Any other value returns a `400`. * The theme is visible only to you. It is never exposed on other users' profiles. ## Examples ```bash theme={null} curl -X PATCH "https://relay.snipp.gg/theme" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"theme": "pastel_blue"}' ``` ## Responses ```json theme={null} { "message": "Theme updated successfully!", "theme": "pastel_blue" } ``` # Unblock User Source: https://docs.snipp.gg/relay-reference/endpoint/unblock POST /unblock Unblock a user by ID. Unblocking a user who is not blocked succeeds without error. ## Examples ```bash theme={null} curl -X POST "https://relay.snipp.gg/unblock" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetId": "123456789012345678"}' ``` ## Responses ```json theme={null} { "blocked": false } ``` Unblocking a user who is not blocked succeeds and includes a `message`: ```json theme={null} { "blocked": false, "message": "User is not blocked." } ``` # Unfollow User Source: https://docs.snipp.gg/relay-reference/endpoint/unfollow POST /unfollow Unfollow a user, or remove one of your own followers. ## Behavior * Pass `targetId` to stop following that user. * Pass `removeFollower` to remove that user from your own followers list. They will need to follow you again to see your posts in their feed. * Exactly one of `targetId` or `removeFollower` must be provided. If both are present, `removeFollower` takes precedence. * Unfollowing a user who is not followed succeeds without error. ## Examples Unfollow a user you currently follow: ```bash theme={null} curl -X POST "https://relay.snipp.gg/unfollow" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"targetId": "123456789012345678"}' ``` Remove a follower: ```bash theme={null} curl -X POST "https://relay.snipp.gg/unfollow" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "Content-Type: application/json" \ -d '{"removeFollower": "123456789012345678"}' ``` ## Responses Unfollow: ```json theme={null} { "following": false } ``` Remove follower: ```json theme={null} { "removed": true } ``` # Upload File(s) Source: https://docs.snipp.gg/relay-reference/endpoint/upload POST /upload Upload between 1 and 50 files in a single request. Every upload automatically creates a post. When sending multiple files, they are grouped into an album by default. ## Headers | Header | Required | Description | | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `relay-key` | Yes | Your Relay key | | `post-privacy` | No | `public`, `unlisted`, or `private` (default) | | `post-title` | No | Post title, max 30 characters | | `post-description` | No | Post description, max 200 characters | | `post-type` | No | `album` (default) or `individual`. Only applies to two or more files. | | `include-metadata` | No | `true` to keep file metadata. Omitted or `false` strips it (default). | | `priority` | No | `false` to opt video uploads out of priority (adaptive) streaming. Omitted or `true` keeps it on (default), subject to your remaining priority minutes. | ## Behavior * **One file:** creates a single post. `post-type` is ignored. * **Two or more files, no header or `album`:** creates one album post containing all files. * **Two or more files, `individual`:** creates a separate post for each file. * **`post-privacy: private`:** the returned `url` (and any `urls` in albums) is a signed URL with a 24-hour expiry. After it expires, call [`GET /posts/{code}`](/relay-reference/endpoint/post) or [`GET /uploads`](/relay-reference/endpoint/uploads) to get a fresh signed URL. * **Video uploads:** `.mp4`, `.mov`, and `.mkv` files are remuxed to MP4 with `+faststart` so the browser can begin playback before the full file has downloaded. `.mov` and `.mkv` uploads are stored (and returned) with an `.mp4` extension. If remuxing fails, the file is rejected as corrupt or unplayable. See [File Types](/concepts/file-types) for details. * **Metadata:** Metadata (camera, GPS location, timestamp) is stripped by default. For images, metadata is removed from PNG, JPG, WebP and GIF at any size, without re-encoding, so the pixels are untouched. Display orientation is preserved on JPG and WebP, and GIF animation and looping are preserved. For videos, container metadata is removed during the MP4 remux. Send `include-metadata: true` to keep it. * **Priority streaming:** Eligible videos are streamed with adaptive bitrate by default, spending [priority minutes](/concepts/plans). Send `priority: false` to upload in standard quality instead and save your minutes. The post's `priority` field reflects whether the video ended up on adaptive streaming. * **Failures:** when every file in the request fails, the response is `400`. For a single-file upload the reason is returned directly as `error` (for example `Weekly usage limit exceeded.`). For multi-file uploads the response is `All files failed to upload.` with a `failed` array naming each file by `index`. ## Examples Upload a single file: ```bash theme={null} curl -X POST "https://relay.snipp.gg/upload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "post-privacy: unlisted" \ -H "post-title: my gaming POV" \ -F "file=@screenshot.png" ``` Upload an album (two or more files default to album): ```bash theme={null} curl -X POST "https://relay.snipp.gg/upload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "post-privacy: public" \ -H "post-title: Game Moderation Evidence" \ -F "file=@photo1.png" \ -F "file=@photo2.png" \ -F "file=@photo3.png" ``` Upload multiple files as individual posts: ```bash theme={null} curl -X POST "https://relay.snipp.gg/upload" \ -H "relay-key: YOUR_RELAY_KEY" \ -H "post-type: individual" \ -F "file=@image1.png" \ -F "file=@image2.png" ``` ## Responses Single file: ```json theme={null} { "message": "Upload successful!", "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "file": { "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "dimensions": { "width": 1920, "height": 1080 } }, "processing_time": 412, "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "unlisted", "priority": false } } ``` `priority` is `true` when the upload is a video that was placed on priority (adaptive) streaming, and `false` otherwise. Album (two or more files, default): one `post` containing every file. ```json theme={null} { "message": "Upload successful!", "files": [ { "index": 0, "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1920, "height": 1080 } }, { "index": 1, "url": "https://i.snipp.gg/123456789012345678/b2c3d4e5f6789012345678abcdef0102.png", "size": 1840221, "size_formatted": "1.76 MB", "mime_type": "image/png", "status": "success", "dimensions": { "width": 1280, "height": 720 } } ], "processing_time": 938, "post": { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "isAlbum": true, "fileCount": 2, "priority": false } } ``` Individual (`post-type: individual`): one entry in `post.posts` per file. If a file fails, a `failed` array is included alongside `files` and the message counts what landed. ```json theme={null} { "message": "1 of 2 files uploaded successfully.", "files": [ { "index": 0, "url": "https://i.snipp.gg/123456789012345678/a1b2c3d4e5f6789012345678abcdef01.png", "size": 2487312, "size_formatted": "2.37 MB", "mime_type": "image/png", "status": "success" } ], "failed": [ { "index": 1, "error": "Weekly usage limit exceeded.", "status": "failed" } ], "processing_time": 938, "post": { "posts": [ { "code": "AbCd1234", "url": "https://snipp.gg/p/AbCd1234", "postPrivacy": "public", "priority": false } ] } } ``` # List Uploads Source: https://docs.snipp.gg/relay-reference/endpoint/uploads GET /uploads Fetch your uploads. Returns all of them by default. Pass `?limit=N` to cap the response (max 1000). ## Behavior * Returns uploads newest first. * Each upload includes `code` for the associated post when one exists. * `isAlbum` is `true` when that upload belongs to an album post. * `title` is the post's title, or `null` when none is set. * `thumbnailUrl` is only present when the post has a thumbnail. * `priority` is `true` when the post's video is served over priority delivery, and `false` for standard delivery. * URLs for private uploads are returned as signed URLs with a 24-hour expiry. After that, request the endpoint again for a fresh signed URL. ## Examples ```bash theme={null} curl -X GET "https://relay.snipp.gg/uploads" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "uploads": [ { "code": "AbCd1234", "isAlbum": true, "url": "https://i.snipp.gg/364836696149458944/16483ec5681c5fc2b5cc08dd49b7dbae", "thumbnailUrl": "https://i.snipp.gg/364836696149458944/thumb/16483ec5681c5fc2b5cc08dd49b7dbae.jpg", "title": "Nice shot", "size": 2487312, "size_formatted": "2.37 MB", "uploaded": "2026-03-27T11:42:18.000Z", "priority": false } ] } ``` # Get Usage History Source: https://docs.snipp.gg/relay-reference/endpoint/usage-history GET /usage-history Fetch your daily upload activity for the last 365 days, along with aggregate stats. This is the same data that powers the Upload Activity heatmap in the Snipp Console, exposed so you can rebuild it in your own tools. ## Behavior * Authenticated with your `relay-key`. Returns activity for the key's owner only. * `history` is ascending by date. Days with no uploads are omitted, so the array is sparse. * `bytes` and `count` reflect successful uploads. A reservation that fails mid-upload is rolled back and does not appear. * `stats.currentStreak` counts consecutive days with uploads ending on the current UTC day. It is `0` if you have not uploaded today. * `stats.biggestDay` is `null` when there is no activity in the window. * All dates are UTC. ## Examples Fetch your usage history: ```bash theme={null} curl -X GET "https://relay.snipp.gg/usage-history" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses ```json theme={null} { "history": [ { "day": "2026-05-12", "bytes": 5242880, "count": 3 }, { "day": "2026-05-14", "bytes": 1048576, "count": 1 } ], "stats": { "totalBytes": 6291456, "totalUploads": 4, "activeDays": 2, "currentStreak": 1, "biggestDay": { "day": "2026-05-12", "bytes": 5242880 }, "windowDays": 365 } } ``` # Get User Source: https://docs.snipp.gg/relay-reference/endpoint/users GET /users/{id} Fetch either your own profile (`@me`) or another user's public profile. ## Behavior * `id=@me` returns your full account profile, including `apiKey`, `relayKey`, `limits`, `subscription`, `uploads`, and your settings. * `theme` is your selected color theme. It is only returned on the `@me` profile, never on other users. It is `null` if you have not picked one. Set it with [`/theme`](/relay-reference/endpoint/theme). * `subscription` is only returned on `@me` and reflects your current plan and lifecycle. See the Subscription block below. * `id=` returns a public-safe profile for that user. * `badges` is always included and follows this order: `verified`, `staff`, `partner`, `bugHunterTier`, `translator`, `plus`. * `bugHunterTier` is an integer from `0` to `2`: `0` is no badge, `1` is Bug Hunter, `2` is Elite Bug Hunter. * `includePosts` controls `publicUploads`. * `includePosts` defaults to `false` for `@me` and `true` for non-self requests. * `postsLimit` limits `publicUploads` to `1` to `50` (default `50`). * Non-self profiles include a `blockedByYou` boolean indicating whether you have blocked that user. * `followerCount` and `followingCount` are always included on both `@me` and non-self profiles. * Non-self profiles include a `following` boolean indicating whether you (the requesting user) follow that user. * If the target user has blocked **you**, the endpoint returns `403` with `"You have been blocked by this user."`. ## Examples Fetch your own profile: ```bash theme={null} curl -X GET "https://relay.snipp.gg/users/@me" \ -H "relay-key: YOUR_RELAY_KEY" ``` Fetch another profile with a smaller public uploads payload: ```bash theme={null} curl -X GET "https://relay.snipp.gg/users/USER_ID?includePosts=true&postsLimit=10" \ -H "relay-key: YOUR_RELAY_KEY" ``` ## Responses `@me` returns your full account profile, including `apiKey`, `relayKey`, and `limits`. ```json theme={null} { "user": { "id": "123456789012345678", "username": "playerone", "avatar": "https://i.snipp.gg/avatars/123456789012345678/avatar.png", "banner": "https://i.snipp.gg/banners/123456789012345678/banner.png", "nickname": "Player One", "bio": "Building things at Snipp", "socials": { "github": "https://github.com/playerone" }, "plus": true, "ultra": false, "verified": false, "staff": false, "partner": false, "bugHunterTier": 0, "translator": false, "suspended": false, "created": "2026-01-04T09:30:00Z", "customEmbed": null, "theme": "pastel_blue", "apiKey": "aB3xK9mP2qR7sT1vW4yZ6cD8fG0hJ5kL", "relayKey": "Zy7wV4uT1sR8qP5nM2kJ9hG6fD3bC0aX", "limits": { "plan": "plus", "maxFileSize": null, "usage": { "used": 5242880, "limit": 21474836480, "usedPercent": 0, "windowStart": "2026-05-12T00:00:00Z", "windowEnd": "2026-05-19T00:00:00Z", "resetsInSeconds": 345600 } }, "subscription": { "plan": "plus", "status": "active", "started_at": "2026-01-04T09:30:00Z", "expires_at": "2027-01-04T09:30:00Z", "cancel_at_period_end": false, "gifted": false }, "followerCount": 128, "followingCount": 47 } } ``` ## Subscription block Returned only on `@me`. Describes the user's current plan and how they got it. | Field | Type | Description | | ---------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `plan` | `"free" \| "plus" \| "ultra"` | Current effective plan. | | `status` | string \| null | `active`, `trialing`, `past_due`, `canceled`, `expired`, `incomplete`, or `gifted` for redeemed/comp'd accounts. `null` for free users. | | `started_at` | timestamp \| null | When the paid subscription began. `null` for free plans, and for gifted plans with no subscription behind them. | | `expires_at` | timestamp \| null | When access ends. For renewing subscriptions this is the next billing date. For gifted plans it is when the gift ends, which is also when a subscription bought during the gift starts billing. `null` means no fixed expiry (e.g. permanent comp). | | `cancel_at_period_end` | boolean | `true` when the user has canceled but still has access until `expires_at`. `false` for gifted plans with no subscription behind them. | | `gifted` | boolean | `true` when the current plan comes from a redeemed gift code or an admin-applied comp, not a Stripe subscription. | A non-self request returns a public-safe profile with `blockedByYou`, `following`, `followerCount`, `followingCount`, and (by default) `publicUploads`. ```json theme={null} { "user": { "id": "987654321098765432", "username": "playertwo", "avatar": "https://i.snipp.gg/avatars/987654321098765432/avatar.png", "banner": null, "nickname": "Player Two", "bio": null, "socials": null, "plus": false, "ultra": false, "verified": false, "staff": false, "partner": false, "bugHunterTier": 1, "translator": false, "created": "2026-02-18T14:00:00Z", "customEmbed": null, "blockedByYou": false, "following": true, "followerCount": 12, "followingCount": 30, "publicUploads": [ { "url": "https://i.snipp.gg/987654321098765432/a1b2c3d4e5f6789012345678abcdef01.png", "code": "AbCd1234", "title": "My screenshot", "description": null, "created": "2026-04-22T18:30:00Z", "public": true, "isAlbum": false } ] } } ``` # Relay Overview Source: https://docs.snipp.gg/relay-reference/introduction Base URL, authentication, endpoints, and behavior for the Snipp Relay API. ## Overview Relay is the Snipp API geared toward the full user experience. It mirrors the public API for uploads and posts, and adds the discover feed, likes, comments, notifications, blocking, profile editing, avatar and banner uploads, and view counting. Relay is intended for first-party clients (for example, the Snipp mobile app). For third-party integrations and SDKs, use the [public API](/api-reference/introduction) at `api.snipp.gg`. ## Base URL The default base URL is: ``` relay.snipp.gg ``` `relay.snipp.gg` is a load balancer sitting in front of the Relay servers. It resolves to the nearest healthy region and connects straight to it, with no per-request size cap, so your file size limit is whatever your plan allows. It is the right default for everything, including uploads. ### Regional endpoints | Region | Host | | ------- | -------------------------- | | EU West | `eu-west-1.relay.snipp.gg` | | US West | `us-west-1.relay.snipp.gg` | Regional endpoints reach the same servers as `relay.snipp.gg` and upload at the same speed, so pinning one buys you no performance. Use them when you want to control which region stores your files, or to force a specific server instead of letting the load balancer choose. To confirm which region served a request, hit [`GET /region`](/relay-reference/endpoint/region) on any host. ### Alternative endpoint `pulsar.snipp.gg` serves the same Relay API over a separate network path. Requests are buffered before they reach the server, so uploads there are capped at **100 MB per request** and large uploads are several times slower. Use it only as a fallback if `relay.snipp.gg` is ever unreachable. ## Authentication Relay uses a `relay-key` header. Each Snipp account has a unique relay key. Treat it like a password. ``` relay-key: YOUR_RELAY_KEY ``` Generate, regenerate, or delete your relay key from [snipp.gg/settings/relay](https://snipp.gg/settings/relay). Keys are 32 alphanumeric characters (`[A-Za-z0-9]`). A missing, empty, or unrecognized relay key returns `401`. ### Team Access Keys [Teams](/concepts/plans#teams) (an Ultra feature) each have a dedicated **access key**. Pass it in the `relay-key` header in place of a personal relay key, and uploads go to the team's shared gallery instead of a personal account. It works on `/upload`, `/appendUpload`, `/editUpload`, and `/deleteUpload`; every other endpoint is personal only and returns `401` for a team key. ``` relay-key: YOUR_TEAM_ACCESS_KEY ``` Key differences when uploading with a team key: * Files are stored in the **team gallery**, not a personal gallery. * Uploads count against the **team's** weekly quota, never a member's personal quota. * Team-key uploads are always created **private**, regardless of the `post-privacy` header, and their privacy cannot be changed afterward. Team posts are never **unlisted** or **public**. * Only the team owner can regenerate or delete the key, from the team's settings page. * If the team's subscription lapses, uploads with the key return `403`. Editing and deleting existing team posts keep working. Anyone who holds a team key can upload with it, so treat it like any other credential and share it only with trusted team members. ## Endpoints | Method | Endpoint | Description | | -------- | ----------------------- | ---------------------------------------------------------------- | | `GET` | `/users/{id}` | Fetch a user profile. Use `@me` for your own account. | | `GET` | `/posts/{code}` | Fetch a single post by share code. | | `POST` | `/upload` | Upload an image or video file. | | `GET` | `/uploads` | List all of your uploads. | | `PATCH` | `/editUpload` | Update title, description, or privacy of a post. | | `POST` | `/appendUpload` | Add files to an existing album. | | `DELETE` | `/deleteUpload` | Delete a file or post. | | `GET` | `/discover` | Browse public uploads from the community. | | `GET` | `/blocks` | List your blocked users. | | `POST` | `/block` | Block a user by ID. | | `POST` | `/unblock` | Unblock a user by ID. | | `POST` | `/follow` | Follow a user by ID. | | `POST` | `/unfollow` | Unfollow a user, or remove one of your own followers. | | `GET` | `/followers` | List the users following a profile. | | `GET` | `/following` | List the users a profile is following. | | `POST` | `/like` | Like or unlike a post. | | `GET` | `/comments` | List comments on a post. | | `POST` | `/comment` | Create a comment on a post. | | `DELETE` | `/comment` | Delete a comment. | | `POST` | `/comment-like` | Like or unlike a comment. | | `GET` | `/notifications` | List your notifications, newest first. | | `GET` | `/notifications/unread` | Number of unread notifications. | | `POST` | `/notifications/read` | Mark every notification as read. | | `GET` | `/notification-prefs` | Read your notification and email preferences. | | `POST` | `/notification-prefs` | Update your notification and email preferences. | | `POST` | `/report-post` | Report a post. | | `POST` | `/report-comment` | Report a comment. | | `POST` | `/report-user` | Report a user. | | `PATCH` | `/profile` | Update profile fields. | | `POST` | `/profile/avatar` | Upload a new avatar. | | `POST` | `/profile/banner` | Upload a new banner. | | `PATCH` | `/theme` | Set your account color theme. PLUS only. | | `GET` | `/gifs` | Browse public GIFs across Snipp, newest first. No auth required. | | `GET` | `/usage-history` | Fetch your daily upload activity for the last 365 days. | | `GET` | `/analytics` | Fetch your analytics over time plus lifetime totals. PLUS only. | | `GET` | `/region` | Return the region serving the request. No auth required. | ## View Counting Relay does not expose a dedicated view endpoint. Views increment automatically when a viewer hits `GET /posts/{code}` or `GET /discover`. To prevent inflation, views are deduplicated per IP and post: an IP can contribute up to 3 counted views per post per hour. Views are only counted on public, non-moderated posts. ## Rate Limits Rate limits apply per endpoint, not per account. See [Rate Limits](/concepts/rate-limits) for the full table and details on handling `429` responses. ## Error Format All errors return a JSON object with an `error` field: ```json theme={null} { "error": "Unauthorized. No relay-key header found." } ``` ## HTTP Status Codes | Code | Meaning | | ----- | --------------------------------------------------------------------- | | `200` | Success | | `400` | Bad request. Invalid input, unsupported file type, or file too large. | | `401` | Unauthorized. Missing or invalid relay key. | | `403` | Forbidden. Account suspended or insufficient permissions. | | `404` | Not found | | `413` | Payload too large. File exceeds your plan's size limit. | | `429` | Rate limit exceeded | | `500` | Internal server error | # SDK Overview Source: https://docs.snipp.gg/sdks/introduction Official API wrappers and integrations for Node.js, Python, Rust, and ShadowPlay. Snipp provides official SDK packages so you can skip raw HTTP calls and focus on building. Each wrapper covers the core API surface with idiomatic patterns for its language. ## Available SDKs `@snipp-gg/snipp`. Zero dependencies, uses native `fetch`. [GitHub](https://github.com/snipp-gg/snipp-js) `snipp`. Built on `requests`. [GitHub](https://github.com/snipp-gg/snipp-py) `snipp-rust`. Async, built on `reqwest`. [GitHub](https://github.com/snipp-gg/snipp-rust) `snipp-shadowplay`. Auto-upload NVIDIA ShadowPlay clips. [GitHub](https://github.com/snipp-gg/ShadowPlay) ## Installation ```bash theme={null} npm install @snipp-gg/snipp ``` ```bash theme={null} pip install snipp ``` Add to your `Cargo.toml`: ```toml theme={null} [dependencies] snipp = "1" tokio = { version = "1", features = ["full"] } ``` ## Quick Comparison ```javascript Node.js theme={null} import { SnippClient } from "@snipp-gg/snipp"; const snipp = new SnippClient({ apiKey: process.env.SNIPP_API_KEY }); const me = await snipp.getUser("@me"); console.log(me.user.username); ``` ```python Python theme={null} from snipp import SnippClient client = SnippClient(api_key="YOUR_API_KEY") me = client.get_user("@me") print(me["user"]["username"]) ``` ```rust Rust theme={null} use snipp::SnippClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = SnippClient::new("YOUR_API_KEY"); let me = client.get_user("@me", None).await?; println!("{}", me.user.username.unwrap_or_default()); Ok(()) } ``` # Node.js Source: https://docs.snipp.gg/sdks/node Official Node.js wrapper for the Snipp API. snipp-gg/snipp-js @snipp-gg/snipp ## Installation ```bash theme={null} npm install @snipp-gg/snipp ``` Requires Node.js 18 or later. Zero dependencies. Uses native `fetch` and `FormData`. ## Getting Started ```javascript theme={null} import { SnippClient } from "@snipp-gg/snipp"; const client = new SnippClient({ apiKey: process.env.SNIPP_API_KEY }); const me = await client.getUser("@me"); console.log(me.user.username); ``` ### Regional endpoints Pass `region` to pin the client to a specific regional host. Uploads run at the same speed either way; this controls which region stores your files. ```javascript theme={null} const client = new SnippClient({ apiKey: process.env.SNIPP_API_KEY, region: "eu-west-1", // or "us-west-1" }); ``` ## Methods ### `getUser(id, options?)` Fetch a user by ID. Pass `"@me"` for the authenticated user. | Option | Type | Description | | -------------- | --------- | ------------------------------------ | | `includePosts` | `boolean` | Include the user's public uploads. | | `postsLimit` | `number` | Number of posts to return (1 to 50). | ```javascript theme={null} const user = await client.getUser("@me", { includePosts: true, postsLimit: 10 }); ``` ### `getPost(code)` Fetch a post by its share code. Team posts are only readable by members of that team, and omit `likeCount`. ```javascript theme={null} const post = await client.getPost("AbCd1234"); console.log(post.post.url); ``` ### `upload(file, options?)` Upload a file. Accepts a `File`, `Blob`, `Buffer`, or `Uint8Array`. | Option | Type | Description | | ------------- | ------------------------------------- | --------------------------------------------------------------------------------- | | `privacy` | `"public" \| "unlisted" \| "private"` | Visibility of the upload. Defaults to `private` when omitted. | | `filename` | `string` | Filename sent with the upload (defaults to `"upload"`). | | `title` | `string` | Optional post title (max 30 chars). | | `description` | `string` | Optional post description (max 200 chars). | | `postType` | `"album" \| "individual"` | `album` (default) or `individual`. Only applies when uploading two or more files. | ```javascript theme={null} import { readFileSync } from "node:fs"; const buffer = readFileSync("./image.png"); const result = await client.upload(buffer, { privacy: "unlisted", filename: "image.png" }); console.log(result.url); ``` ### `listUploads(options?)` List the authenticated user's recent uploads. Defaults to 30. Pass `limit` to control the count (max 1000). | Option | Type | Description | | ------- | -------- | ----------------------------------- | | `limit` | `number` | Maximum uploads to return (1-1000). | ```javascript theme={null} const uploads = await client.listUploads({ limit: 100 }); ``` ### `editUpload(code, options)` Edit an existing upload's title, description, or privacy. | Option | Type | Description | | ------------- | ------------------------------------- | ------------------------------------------------------- | | `title` | `string` | New title (max 30 chars). Empty string to clear. | | `description` | `string` | New description (max 200 chars). Empty string to clear. | | `privacy` | `"public" \| "unlisted" \| "private"` | New visibility setting. | ```javascript theme={null} await client.editUpload("AbCd1234", { title: "New title", privacy: "public" }); ``` ### `appendUpload(code, files, options?)` Add one or more files to an existing album post. The post's share code, privacy, title, and description are preserved. Albums cap at 50 files total. Requests that would exceed the cap are rejected. New files inherit the post's privacy. Returned URLs are signed with a 24-hour expiry for private posts. | Parameter | Type | Description | | ------------------- | --------------------------------------------- | ------------------------------------ | | `code` | `string` | Share code of the post to append to. | | `files` | `Array` | One or more files to append. | | `options.filenames` | `string[]` | Optional filenames (one per file). | ```javascript theme={null} import { readFileSync } from "node:fs"; const a = readFileSync("./photo-a.png"); const b = readFileSync("./photo-b.png"); const result = await client.appendUpload("AbCd1234", [a, b], { filenames: ["photo-a.png", "photo-b.png"], }); console.log(result.post.fileCount); ``` ### `deleteUpload(filename)` Delete an upload by its filename. On albums, only that file is removed; on single-file posts the entire post is deleted. ```javascript theme={null} await client.deleteUpload("a3f7b2c91d4e8f0612ab34cd56ef7890.png"); ``` ### `reportPost(code, reason?)` Report a post, with an optional reason (max 200 chars). ```javascript theme={null} await client.reportPost("AbC123", "Spam"); ``` ## Error Handling All API errors throw a `SnippError` with `status`, `message`, and `body` properties. `body` is the parsed JSON error response, or `null` when the response was not JSON. It carries the fields the API sends alongside `error`, such as `suspended` on a suspended user or `moderated` on a moderated post. ```javascript theme={null} import { SnippClient, SnippError } from "@snipp-gg/snipp"; try { await client.getUser("987654321098765432"); } catch (err) { if (err instanceof SnippError) { if (err.body?.suspended) { console.error(`${err.body.username} is suspended`); } else { console.error(err.status, err.message); } } } ``` # Python Source: https://docs.snipp.gg/sdks/python Official Python wrapper for the Snipp API. snipp-gg/snipp-py snipp ## Installation ```bash theme={null} pip install snipp ``` Requires Python 3.9 or later. Depends on `requests`. ## Getting Started ```python theme={null} from snipp import SnippClient client = SnippClient(api_key="YOUR_API_KEY") me = client.get_user("@me") print(me["user"]["username"]) ``` ### Regional endpoints Pass `region` to pin the client to a specific regional host. Uploads run at the same speed either way; this controls which region stores your files. ```python theme={null} client = SnippClient(api_key="YOUR_API_KEY", region="eu-west-1") # or "us-west-1" ``` ## Methods ### `get_user(user_id, include_posts=None, posts_limit=None)` Fetch a user by ID. Pass `"@me"` for the authenticated user. | Parameter | Type | Description | | --------------- | -------------- | ------------------------------------ | | `user_id` | `str` | User ID or `"@me"`. | | `include_posts` | `bool \| None` | Include the user's public uploads. | | `posts_limit` | `int \| None` | Number of posts to return (1 to 50). | ```python theme={null} user = client.get_user("@me", include_posts=True, posts_limit=10) ``` ### `get_post(code)` Fetch a post by its share code. Team posts are only readable by members of that team, and omit `likeCount`. | Parameter | Type | Description | | --------- | ----- | --------------------------- | | `code` | `str` | The share code of the post. | ```python theme={null} post = client.get_post("AbCd1234") print(post["post"]["url"]) ``` ### `upload(file, privacy=None, title=None, description=None, post_type=None)` Upload a file. Accepts a file path string, bytes, or a file-like object. | Parameter | Type | Description | | ------------- | -------------------------- | ------------------------------------------------------------------------------------- | | `file` | `str \| bytes \| BinaryIO` | File path, raw bytes, or file object. | | `privacy` | `str \| None` | `"public"`, `"unlisted"`, or `"private"`. Defaults to `private` when omitted. | | `title` | `str \| None` | Optional post title (max 30 chars). | | `description` | `str \| None` | Optional post description (max 200 chars). | | `post_type` | `str \| None` | `"album"` (default) or `"individual"`. Only applies when uploading two or more files. | ```python theme={null} result = client.upload("image.png", privacy="unlisted", title="My screenshot") print(result["url"]) ``` ### `list_uploads(limit=None)` List the authenticated user's recent uploads. Defaults to 30. Pass `limit` to control the count (max 1000). | Argument | Type | Description | | -------- | ------------- | ----------------------------------- | | `limit` | `int \| None` | Maximum uploads to return (1-1000). | ```python theme={null} uploads = client.list_uploads(limit=100) ``` ### `edit_upload(code, title=None, description=None, privacy=None)` Edit an existing upload's title, description, or privacy. | Parameter | Type | Description | | ------------- | ------------- | ------------------------------------------------------- | | `code` | `str` | The share code of the upload. | | `title` | `str \| None` | New title (max 30 chars). Empty string to clear. | | `description` | `str \| None` | New description (max 200 chars). Empty string to clear. | | `privacy` | `str \| None` | `"public"`, `"unlisted"`, or `"private"`. | ```python theme={null} client.edit_upload("AbCd1234", title="New title", privacy="public") ``` ### `append_upload(code, files)` Add one or more files to an existing album post. The post's share code, privacy, title, and description are preserved. Albums cap at 50 files total. Requests that would exceed the cap are rejected. New files inherit the post's privacy. Returned URLs are signed with a 24-hour expiry for private posts. | Parameter | Type | Description | | --------- | -------------------------------- | ---------------------------------------------------------------------------------- | | `code` | `str` | Share code of the post to append to. | | `files` | `list[str \| bytes \| BinaryIO]` | One or more files. Each item may be a file path, raw bytes, or a file-like object. | ```python theme={null} result = client.append_upload("AbCd1234", ["photo-a.png", "photo-b.png"]) print(result["post"]["fileCount"]) ``` ### `delete_upload(filename)` Delete an upload by its filename. On albums, only that file is removed; on single-file posts the entire post is deleted. ```python theme={null} client.delete_upload("a3f7b2c91d4e8f0612ab34cd56ef7890.png") ``` ### `report_post(code, reason="")` Report a post, with an optional reason (max 200 chars). ```python theme={null} client.report_post("AbC123", "Spam") ``` ## Error Handling All API errors raise a `SnippError` with `status`, `message`, and `body` attributes. `body` is the parsed JSON error response, or `None` when the response was not JSON. It carries the fields the API sends alongside `error`, such as `suspended` on a suspended user or `moderated` on a moderated post. ```python theme={null} from snipp import SnippClient, SnippError try: client.get_user("987654321098765432") except SnippError as err: if err.body and err.body.get("suspended"): print(f"{err.body['username']} is suspended") else: print(err.status, err.message) ``` # Rust Source: https://docs.snipp.gg/sdks/rust Official Rust wrapper for the Snipp API. snipp-gg/snipp-rust crates.io/crates/snipp ## Installation Add to your `Cargo.toml`: ```toml theme={null} [dependencies] snipp = "2" tokio = { version = "1", features = ["full"] } ``` ## Getting Started ```rust theme={null} use snipp::SnippClient; #[tokio::main] async fn main() -> Result<(), Box> { let client = SnippClient::new("YOUR_API_KEY"); let me = client.get_user("@me", None).await?; println!("{}", me.user.username.unwrap_or_default()); Ok(()) } ``` ### Regional endpoints Use `with_region` to pin the client to a specific regional host. Uploads run at the same speed either way; this controls which region stores your files. ```rust theme={null} let client = SnippClient::with_region("YOUR_API_KEY", "eu-west-1")?; // or "us-west-1" ``` ## Methods All methods are `async` and return `Result`. ### `get_user(id, options)` Fetch a user by ID. Pass `"@me"` for the authenticated user. ```rust theme={null} use snipp::GetUserOptions; // Without options let user = client.get_user("@me", None).await?; // With options let opts = GetUserOptions { include_posts: Some(true), posts_limit: Some(10), }; let user = client.get_user("@me", Some(opts)).await?; ``` ### `get_post(code)` Fetch a post by its share code. Team posts are only readable by members of that team, and carry no `like_count`. ```rust theme={null} let post = client.get_post("AbCd1234").await?; println!("{:?}", post.post.url); ``` ### `upload(path, options)` Upload a file from disk. `privacy` defaults to `private` when omitted. ```rust theme={null} use snipp::{Privacy, UploadOptions}; let opts = UploadOptions { privacy: Some(Privacy::Unlisted), title: Some("My screenshot".into()), ..Default::default() }; let result = client.upload("image.png", Some(opts)).await?; println!("{}", result.url.unwrap_or_default()); ``` ### `list_uploads(limit)` List the authenticated user's recent uploads. Pass `Some(N)` for `limit` to control the count (max 1000), or `None` for the default (30). ```rust theme={null} let uploads = client.list_uploads(Some(100)).await?; ``` ### `edit_upload(code, options)` Edit an existing upload's title, description, or privacy. Empty strings clear the title or description. ```rust theme={null} use snipp::{EditUploadOptions, Privacy}; let opts = EditUploadOptions { title: Some("New title".into()), privacy: Some(Privacy::Public), ..Default::default() }; client.edit_upload("AbCd1234", opts).await?; ``` ### `append_upload(code, file_paths)` Add one or more files to an existing album post. The post's share code, privacy, title, and description are preserved. Albums cap at 50 files total. Requests that would exceed the cap are rejected. New files inherit the post's privacy. Returned URLs are signed with a 24-hour expiry for private posts. ```rust theme={null} let result = client .append_upload("AbCd1234", &["photo-a.png", "photo-b.png"]) .await?; if let Some(post) = result.post { println!("album now has {:?} files", post.file_count); } ``` ### `delete_upload(filename)` Delete an upload by its filename. On albums, only that file is removed; on single-file posts the entire post is deleted. ```rust theme={null} client.delete_upload("a3f7b2c91d4e8f0612ab34cd56ef7890.png").await?; ``` ### `report_post(code, reason)` Report a post. Pass an empty string to omit the reason (max 200 chars). ```rust theme={null} client.report_post("AbC123", "Spam").await?; ``` ## Error Handling All errors are returned as `SnippError`, which covers API errors, HTTP errors, local input validation, deserialization failures, and IO errors. `SnippError::Api` carries `status`, `message`, and `body`. `body` is the parsed JSON error response, or `None` when the response was not JSON. It carries the fields the API sends alongside `error`, such as `suspended` on a suspended user or `moderated` on a moderated post. ```rust theme={null} use snipp::SnippError; match client.get_user("987654321098765432", None).await { Ok(user) => println!("{:?}", user), Err(SnippError::Api { status, message, body }) => { let suspended = body .as_ref() .and_then(|b| b.get("suspended")?.as_bool()) .unwrap_or(false); if suspended { eprintln!("user is suspended"); } else { eprintln!("{status}: {message}"); } } Err(e) => eprintln!("Error: {e}"), } ``` # ShadowPlay Source: https://docs.snipp.gg/sdks/shadowplay Auto-upload NVIDIA ShadowPlay clips to Snipp. snipp-gg/ShadowPlay snipp-shadowplay ## Installation ```bash theme={null} pip install snipp-shadowplay ``` Requires Python 3.9 or later. Dependencies: `watchdog`, `requests`, `pyperclip`, `colorama`. ## Configuration 1. **Get your API key.** Generate a key from your account settings. 2. **Set your API key** as an environment variable: ```cmd theme={null} setx SNIPP_API_KEY YOUR_API_KEY ``` ```powershell theme={null} [System.Environment]::SetEnvironmentVariable("SNIPP_API_KEY", "YOUR_API_KEY", "User") ``` Save your key to `~/.snipp_uploader`: ```bash theme={null} echo "YOUR_API_KEY" > ~/.snipp_uploader ``` ## Getting Started Once installed and configured, run: ```cmd theme={null} snipp ``` The watcher monitors your `~/Videos` folder for new `.mp4` files. When a clip is saved by ShadowPlay, it is automatically uploaded to Snipp and the shareable URL is copied to your clipboard. ## How It Works 1. **File detection.** Watches the `~/Videos` directory (recursively) using `watchdog` for new `.mp4` files. 2. **Completion wait.** Waits for the file to finish being written to disk before uploading. 3. **Upload.** Sends the clip to the Snipp API as a multipart upload with `unlisted` privacy. 4. **Clipboard.** Copies the returned URL to your clipboard via `pyperclip`. ## Limits * Maximum file size: **1 GB** * Supported format: `.mp4` * Privacy: uploads default to **unlisted** ## Error Handling Errors are printed to the console with color-coded messages: | Color | Meaning | | ------ | -------------------------------------- | | Blue | Informational (startup, file detected) | | Yellow | Warnings (file still processing) | | Green | Success (uploaded with URL) | | Red | Errors (upload failed, file too large) |