Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/snaplab-dev:snaplab-dev-free-tools-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: snaplab Free tools API
version: 1.0.0
summary: Turn any URL or HTML into a screenshot, a PDF, or a social image.
description: 'snaplab renders web pages with a real headless Chromium and hands you the bytes.
- `POST|GET /api/render` — URL **or** raw HTML → PNG / JPEG / WebP / AVIF / PDF, viewport or full page, retina, dark mode, element clipping.
- `POST|GET /api/render/image` — HTML (or a `{{ template }}` + `data`) → a fixed-size image. The Open Graph card path.
- `POST /api/render/preview` — unauthenticated, rate-limited demo render.
- `POST /api/tools/*` — key-less public tools, 20 renders/day/IP, always answered with a hosted URL.
- `/mcp` — a stateless Streamable-HTTP MCP server exposing `screenshot`, `pdf`, `html_to_image` and `usage`.
## Authentication
Every endpoint except `/api/health`, `/api/render/preview` and `/api/tools/*` needs an API key. Keys look like `sk_…`
and are created in the dashboard or through `POST /api/keys`.
1. `Authorization: Bearer sk_…` — the header form, accepted everywhere.
2. Signed query parameters — `GET /api/render` and `GET /api/render/image` only. See below.
3. The browser session cookie, for the dashboard itself.
### Signed URLs
Putting a raw key in an `` leaks it. Sign the URL instead: append `keyId` (the API key''s id) and
`sig=hex(HMAC_SHA256(signing_secret, canonical))`, optionally with an `expires` Unix timestamp. The canonical string is
every query pair except `sig`, sorted by name then value and joined with `&`. The per-key signing secret and a URL
builder live in the dashboard under Keys.
## Options
Options are camelCase and identical between the JSON body and the query string. In a query string, booleans are
`true`/`1` and `false`/`0`, `hideSelectors` is a comma-separated list, and PDF options take either the dotted form
(`pdf.format=A4`) or the flat alias (`pdfFormat=A4`).
## Caching and quota
Identical options are served from a shared cache for 24h. **Cache hits and failed renders do not count against your
quota** — only fresh, successful renders do. Renders carry `X-Snaplab-Cache`, `X-Snaplab-Duration-Ms`,
`X-Snaplab-Fingerprint` and `X-Snaplab-Quota-Remaining`.
Pass `cache: false` to force a fresh render.
## Errors
Errors are JSON: `{"error":"…","code":"…"}`. Messages are for humans; codes are stable.
| Status | Code | Meaning |
| --- | --- | --- |
| 400 | — | A parameter is missing, malformed, out of range, or the target is a private/blocked address. |
| 401 | `unauthorized` | Missing, unknown or revoked API key; or a bad signature. |
| 402 | `payment_required` | The subscription is past due. |
| 422 | `selector_not_found` | The page rendered but `selector` matched nothing. |
| 429 | `quota_exceeded` | The monthly render allowance is used up. |
| 429 | — | Too many requests per minute for your plan. |
| 502 | `upstream` | The target site was unreachable. |
| 503 | `capacity` / `not_ready` | The render queue is saturated, or the browsers are still starting. Retry shortly. |
| 504 | `timeout` | The page did not finish in time. |
## Plans
| Plan | Renders / month | Price | Rate | Concurrency |
| --- | --- | --- | --- | --- |
| free | 100 | $0 | 30/min | 1 |
| starter | 2,000 | $9 | 120/min | 3 |
| pro | 10,000 | $29 | 300/min | 5 |
| business | 40,000 | $79 | 600/min | 10 |'
termsOfService: https://snaplab.dev/terms
contact:
name: snaplab support
url: https://snaplab.dev/docs
email: snaplab.dev
license:
name: MIT
identifier: MIT
servers:
- url: https://snaplab.dev
description: Production
security:
- bearerAuth: []
tags:
- name: Free tools
description: Key-less public endpoints behind the free tool pages.
paths:
/api/tools/screenshot:
post:
tags:
- Free tools
operationId: toolScreenshot
summary: Free screenshot
description: 'Public and key-less: 20 renders per day per IP, viewports capped at 1600px, and always answered with a hosted URL.'
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
html:
type: string
maxLength: 200000
fullPage:
type: boolean
default: true
format:
type: string
enum:
- png
- jpeg
- webp
default: png
width:
type: integer
maximum: 1600
height:
type: integer
maximum: 1600
darkMode:
type: boolean
mobile:
type: boolean
dismissBanners:
type: boolean
default: true
blockAds:
type: boolean
default: false
additionalProperties: false
example:
url: https://example.com
fullPage: true
responses:
'200':
description: A hosted render plus the caller's remaining daily budget.
content:
application/json:
schema:
$ref: '#/components/schemas/ToolResult'
'400':
description: Invalid options, or a blocked target.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: The free daily limit for this IP is spent (`tool_limit_reached`).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Renderer busy.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/tools/pdf:
post:
tags:
- Free tools
operationId: toolPdf
summary: Free HTML/URL → PDF
description: 'Public and key-less: 20 renders per day per IP, viewports capped at 1600px, and always answered with a hosted URL.'
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
html:
type: string
maxLength: 200000
format:
type: string
enum:
- Letter
- Legal
- Tabloid
- A3
- A4
- A5
default: A4
landscape:
type: boolean
default: false
darkMode:
type: boolean
dismissBanners:
type: boolean
default: true
blockAds:
type: boolean
default: false
additionalProperties: false
example:
url: https://example.com
format: A4
responses:
'200':
description: A hosted render plus the caller's remaining daily budget.
content:
application/json:
schema:
$ref: '#/components/schemas/ToolResult'
'400':
description: Invalid options, or a blocked target.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: The free daily limit for this IP is spent (`tool_limit_reached`).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Renderer busy.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/tools/image:
post:
tags:
- Free tools
operationId: toolImage
summary: Free OG-image generator
description: 'Public and key-less: 20 renders per day per IP, viewports capped at 1600px, and always answered with a hosted URL.'
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- html
properties:
html:
type: string
maxLength: 200000
css:
type: string
maxLength: 50000
googleFonts:
type: string
maxLength: 300
width:
type: integer
maximum: 1600
default: 1200
height:
type: integer
maximum: 1600
default: 630
format:
type: string
enum:
- png
- jpeg
- webp
default: png
additionalProperties: false
example:
html: <div style='padding:80px;font:700 64px sans-serif'>Hello</div>
width: 1200
height: 630
responses:
'200':
description: A hosted render plus the caller's remaining daily budget.
content:
application/json:
schema:
$ref: '#/components/schemas/ToolResult'
'400':
description: Invalid options, or a blocked target.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: The free daily limit for this IP is spent (`tool_limit_reached`).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Renderer busy.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Error:
type: object
required:
- error
properties:
error:
type: string
description: Human-readable message.
code:
type: string
description: Stable machine code, when one applies.
retryable:
type: boolean
issues:
type: array
items:
type: object
additionalProperties: true
description: Zod validation issues, for 400s.
upgradeUrl:
type: string
ToolResult:
allOf:
- $ref: '#/components/schemas/HostedRender'
- type: object
properties:
kind:
type: string
enum:
- screenshot
- pdf
- image
remainingToday:
type: integer
dailyLimit:
type: integer
example: 20
HostedRender:
type: object
description: 'Answer for `responseType: "json"` — the render is stored and served from a public, immutable URL.'
required:
- url
- id
- format
- bytes
properties:
url:
type: string
format: uri
example: https://snaplab.dev/i/c/V1StGXR8_Z5j
id:
type: string
example: V1StGXR8_Z5j
format:
type: string
example: png
width:
type: integer
height:
type: integer
bytes:
type: integer
cacheHit:
type: boolean
durationMs:
type: integer
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: sk_<40 chars>
description: 'Send the key as `Authorization: Bearer sk_…`. This is also how the MCP server at `/mcp` authenticates.'
signedKeyId:
type: apiKey
in: query
name: keyId
description: 'Signed-URL auth (GET renders only): the API key''s **id**, not its secret. Pair with `sig`.'
signedSignature:
type: apiKey
in: query
name: sig
description: '`hex(HMAC_SHA256(signingSecret, canonical))`, where canonical is every query pair except `sig`, sorted by name then value and joined with `&`. Add `expires` (Unix seconds) to time-limit the URL.'
externalDocs:
description: snaplab documentation
url: https://snaplab.dev/docs