snaplab-dev Free tools API

Key-less public endpoints behind the free tool pages.

Operations 3

POST /api/tools/screenshot Free screenshot #
POST /api/tools/pdf Free HTML/URL → PDF #
POST /api/tools/image Free OG-image generator #

Work with this as data

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 Specification

snaplab-dev-free-tools-api-openapi.yml Raw ↑
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