Skip to content

Type an option name like full_page, an error code, or a topic.

Authentication and API keys

How to send your access key, what the secret key and signatures are for, and how to rotate, revoke and protect your keys.

Send your access key in a header, a named line that travels with the request, and the request is yours.

Request
curl "https://curlshot.com/api/v1/screenshot?url=https://example.com" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  --output example.png

You get example.png, a screenshot of example.com. Without a key you get this instead:

Response, status 401
{
  "error_code": "access_key_required",
  "error_message": "An access key is required. Pass `access_key` or the `X-Access-Key` header.",
  "documentation_url": "https://curlshot.com/docs/errors#access_key_required"
}

Every request to CurlShot needs an access key. It tells us whose quota, the monthly number of screenshots in your plan, the screenshot counts against.

#Where to find your key

  1. Create an account, or log in.
  2. Open API keys in the dashboard.
  3. Copy the access key.

A key works once the email address of your account is confirmed. Until then, requests answer email_not_verified.

You can have more than one key. Give each one a name, such as "production" or "staging", so you can tell them apart later.

#Three ways to send the key

All three do the same thing. Pick the one that fits your code.

WhereWhat you sendGood for
Query stringaccess_key=YOUR_ACCESS_KEYQuick tests in a terminal.
HeaderX-Access-Key: YOUR_ACCESS_KEYServer code. The key stays out of the URL.
HeaderAuthorization: Bearer YOUR_ACCESS_KEYHTTP clients and tools that already have a "bearer token" setting.

If a request carries the key in more than one place, the query string wins.

Here is the header version in each language:

curl "https://curlshot.com/api/v1/screenshot?url=https://example.com" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  --output example.png

The samples read the key from an environment variable. That is a setting stored on your server, outside your code, so the key never ends up in your repository.

#Limits belong to the account, not the key

All your keys share one set of limits: the monthly quota, the number of requests per minute, and the number of renders that may run at the same time.

So a second key does not buy more requests per minute. Ten keys on one account have the same per-minute limit as one key.

Extra keys are for organisation and safety: one per app or environment, each one revocable on its own. Usage and limits explains the three limits.

#Access key and secret key

Each key you create is a pair: an access key and a secret key.

The access key identifies you. It travels with every request. Treat it like a password, even though it is the "public" half of the pair.

The secret key never travels. You use it on your own server to sign things, and we use our copy to check the signature. A signature is a short code computed from the request and the secret, so only someone who holds the secret can produce it.

You need the secret key in two places:

  • Signed links, when a screenshot URL has to appear in a web page or an email.
  • Webhooks, to check that a message really came from us.

If you only call the API from your server, you can ignore the secret key.

#Keep keys off the browser

Anything in a web page can be read by the person looking at it. That includes JavaScript files, HTML attributes and network requests.

So this is not safe in a public page:

Do not do this
<img src="https://curlshot.com/api/v1/screenshot?access_key=YOUR_ACCESS_KEY&url=https://example.com">

A visitor can copy the key and spend your quota on their own screenshots.

You have two safe choices:

  • Call the API from your server and send the image to the browser yourself.
  • Use a signed link. The signature covers every parameter, so a visitor cannot change the page being captured.

#What a signature covers

A signed link has one more parameter, signature. You compute it on your server from the link's parameters and your secret key. Signed links shows how, with code.

Three rules decide what a signed link can do:

  • Nothing in it can be changed. Edit, add or remove a parameter and the answer is signature_invalid with status 403.
  • It works for one endpoint. An endpoint is one address of the API, such as /screenshot or /usage. A signed screenshot link takes that one screenshot. It cannot be reused to read your usage, your jobs or your stored files.
  • It can have an end date. Add expires before you sign.

#expires

The moment a link stops working, as a Unix time in seconds. Unix time counts the seconds since 1 January 1970, so 1798761600 is 1 January 2027, 00:00 UTC.

There is no default. Leave it out and the link has no end date.

The link works until that moment and never after. Because expires is part of the signed parameters, nobody can move the date. An expired link answers like this:

Response, status 403
{
  "error_code": "request_expired",
  "error_message": "This request has expired: its `expires` time is in the past. Create a new link with a later `expires`.",
  "documentation_url": "https://curlshot.com/docs/errors#request_expired"
}

#Accept signed requests only

Each key has a Require signature setting in the dashboard. Turn it on, and that key refuses every request that has no valid signature.

An unsigned request then fails like this:

Response, status 403
{
  "error_code": "signature_required",
  "error_message": "This access key only accepts signed requests. Add a `signature` parameter.",
  "documentation_url": "https://curlshot.com/docs/errors#signature_required"
}

This is the right setting for a key that appears in public pages. Even if someone copies the access key from a link, they cannot make new requests with it.

Without the setting, someone could delete the signature from your link and send their own parameters with your access key.

#Rotate a key

Rotating means swapping an old key for a new one. Do it on a schedule, or right away if a key may have leaked.

  1. Create a new key in the dashboard.
  2. Put the new key in your server settings and deploy.
  3. Check that requests work with the new key.
  4. Revoke the old key.

Doing it in this order means there is no moment when your app has no working key.

#Revoke a key

Revoking switches a key off for good. Open API keys, pick the key and revoke it.

From then on, every request with that key fails with access_key_invalid and status 401. Signed links made with that key stop working too.

#Where to go next