Skip to content
DNSPeekDocs

Account & API

API keys and REST API v1

Available on the Pro, Agency and Provider plansUpdated September 2026

Create a preview for every pull request, every staging deploy or every site in a hosting migration, straight from a script or your CI. The REST API manages preview links, runs QA reports and checks DNS propagation with the same rules and plan limits as the dashboard. Every endpoint, field and error code is in the REST API v1 reference.

On this page

Create an API key

  1. Open API Keys in the sidebar

    Owners and admins of a workspace on a plan with API access can create keys.

  2. Name the key and choose its access, expiry and IPs

    Use a name that says where it is used, such as “Production” or “CI/CD”. Pick Read & write or Read only, when the key expires (30 days, 90 days, 1 year or never) and, optionally, the IP addresses it may be used from.

  3. Copy the key now

    Keys start with dpk_ and are shown only once. Store the key in your CI’s secret store, never in code.

The list shows each key’s name, prefix, access, IP limit, expiry and its last use: when, from which network (the last part of the address is hidden) and with which client. Revoke a key the moment you no longer need it or suspect it leaked.

Access: read only or read and write

AccessWhat the key can do
Read & writeEverything: create, change and delete links, and start QA reports.
Read onlyOnly requests that read (GET), such as listing links, reading QA reports or checking DNS. Anything that changes data answers 403 with API_KEY_SCOPE.

Give dashboards, reports and monitoring a read-only key. Keys created before access levels existed have read and write access.

Expiry

A key can expire 30 days, 90 days or 1 year after it is created, or never. The person who created it gets a reminder 7 days and 1 day before (by email and in the notification bell; turn either off in your notification settings), and another when it has expired. An expired key answers 401 with API_KEY_EXPIRED: create a new key, update your secrets, then revoke the old one.

Allowed IP addresses

Limit a key to your servers or CI runners by listing up to 20 IPv4 or IPv6 addresses or CIDR ranges, such as 203.0.113.10 or 198.51.100.0/24. A request from any other address answers 403 with API_KEY_IP_NOT_ALLOWED, even with the right key. Leave the list empty to allow any address.

Your personal IP allowlist (Settings → Security) and your team’s Network access still apply on top of the key’s own list.

Rotate a key

Rotate on the API Keys page gives a key a new secret and keeps its name, prefix, access, expiry and IP list. The new key is shown once, and the old secret stops working immediately, so update your secrets right away. Rotating is recorded in the team’s activity log.

Authenticate

Send the key as a bearer token on every request. All endpoints live under https://api.dnspeek.link/api/v1.

List links
curl https://api.dnspeek.link/api/v1/links \
  -H "Authorization: Bearer $DNSPEEK_API_KEY"

Endpoints

RequestAccessWhat it does
GET /api/v1/linksreadLinks of the workspace, newest first, one page at a time: shared links plus the key creator’s private links.
POST /api/v1/linkswriteCreates a link and returns it.
GET /api/v1/links/:aliasreadOne link with its preview URL, status and settings.
PATCH /api/v1/links/:aliaswriteTurns the preview on or off, sets or removes the password and expiry, or moves it to another server IP.
DELETE /api/v1/links/:aliaswriteDeletes a link (200 with an empty body).
POST /api/v1/links/:alias/qa-reportswriteStarts a QA report of the new server (uses the monthly quota).
GET /api/v1/links/:alias/qa-reportsreadQA reports of a link, newest first.
GET /api/v1/qa-reports/:idreadStatus, score, verdict and finding counts of a QA report.
GET /api/v1/dns/propagation?domain=&expected_ip=readWhether public resolvers already return the new IP.

The machine-readable spec is at https://api.dnspeek.link/api/v1/openapi.json (OpenAPI 3), and a ready-made Postman collection at https://api.dnspeek.link/api/v1/postman.json.

Create a link

Fields for creating a link (JSON body):

FieldRule
targetDomainRequired. The site’s domain, up to 255 characters.
targetIpRequired. Public IP address of the new server.
aliasOptional. 3–50 lowercase letters, digits or dashes; generated when omitted. 409 if taken.
httpPortOptional. 1–65535, default 80.
httpsPortOptional. 1–65535, default 443.
sslModeOptional. auto (default), skip or verify.
passwordOptional. Up to 128 characters. The response only says hasPassword.
expiresAtOptional. ISO 8601 date-time. Plan rules and team policies apply.
spaceIdOptional. ID of a space in the key’s workspace.
visibilityOptional. workspace (default) or private: only the key’s creator then sees the link in the dashboard and API. The preview URL works either way.
Create a link
curl -X POST https://api.dnspeek.link/api/v1/links \
  -H "Authorization: Bearer $DNSPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: deploy-2026-09-28-01" \
  -d '{
    "targetDomain": "shop.example.com",
    "targetIp": "203.0.113.10",
    "alias": "shop-preview",
    "password": "client-only",
    "expiresAt": "2026-12-31T00:00:00Z"
  }'
Response (abridged)
{
  "id": "0b6f3c1e-…",
  "alias": "shop-preview",
  "status": "active",
  "previewUrl": "https://shop-preview.dnspeek-preview.link/",
  "dashboardUrl": "https://dnspeek.link/links/0b6f3c1e-…",
  "targetDomain": "shop.example.com",
  "targetIp": "203.0.113.10",
  "sslMode": "auto",
  "hasPassword": true,
  "disabled": false,
  "expiresAt": "2026-12-31T00:00:00.000Z",
  "visibility": "workspace",
  "createdAt": "2026-09-28T10:00:00.000Z",
  "targetHints": []
}

status is active, disabled (turned off by your team), blocked (disabled by DNSPeek, see blockedReason) or expired. Unknown fields are rejected.

Change a link

Send only the fields you want to change: enabled, password (null removes it), expiresAt (null removes it), targetIp, httpPort, httpsPort, sslMode or visibility. A new server IP re-runs the server check. The same checks as in the dashboard apply: public IPs only, the platform blocklist, team policies and plan limits. Only the link’s creator can change visibility (403 otherwise). Other members’ private links answer 404 LINK_NOT_FOUND, as if they did not exist.

Move the preview to the new server
curl -X PATCH https://api.dnspeek.link/api/v1/links/shop-preview \
  -H "Authorization: Bearer $DNSPEEK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "targetIp": "203.0.113.20", "enabled": true }'
Delete a link
curl -X DELETE https://api.dnspeek.link/api/v1/links/shop-preview \
  -H "Authorization: Bearer $DNSPEEK_API_KEY"

Run a QA report

A QA report crawls the site on the new server and lists broken pages, redirects, mixed content and other launch problems (see QA report). Starting one uses a QA report of your plan’s monthly quota; when it is used up the API answers 402 with QA_QUOTA_EXCEEDED.

Start a report
curl -X POST https://api.dnspeek.link/api/v1/links/shop-preview/qa-reports \
  -H "Authorization: Bearer $DNSPEEK_API_KEY"

Send run settings in the body to change the crawl; fields you leave out use the link’s default run settings:

Start a report with run settings
curl -X POST https://api.dnspeek.link/api/v1/links/shop-preview/qa-reports   -H "Authorization: Bearer $DNSPEEK_API_KEY"   -H "Content-Type: application/json"   -d '{"maxPages": 50, "viewports": "both", "excludePaths": ["/cart/**"], "stagingAuth": {"type": "header", "name": "X-Staging-Token", "value": "'"$STAGING_TOKEN"'"}}'

Settings beyond your plan (more pages than it allows, "crawlSpeed": "fast" without the feature) answer 402 with PLAN_LIMIT; invalid patterns or header names answer 400 with VALIDATION_FAILED. Neither uses the quota. Staging credentials are stored encrypted, sent only to the link’s domain on the new server and never returned; the report’s settings field shows the settings used without them.

Poll GET /api/v1/qa-reports/:id every few seconds until status is completed or failed (a run usually takes 1–5 minutes). A completed report has a score (0–100), finding counts and a verdict: not_ready when there is any critical finding, ready_with_warnings when there are warnings, otherwise ready. reportUrl opens the full report in the dashboard; shareUrl is the read-only link you can send to a client.

Check DNS propagation

Has the switch reached the world?
curl "https://api.dnspeek.link/api/v1/dns/propagation?domain=shop.example.com&expected_ip=203.0.113.20" \
  -H "Authorization: Bearer $DNSPEEK_API_KEY"

The answer lists each public resolver and whether it already returns the new IP, the share that does (percent), and propagated once at least 90% agree. Results are cached for 60 seconds, and each key can make 10 checks a minute. See DNS propagation for what the numbers mean.

Pagination

Lists return { items, total, limit, nextCursor }, newest first. Ask for up to 100 items with ?limit=, then pass nextCursor back as ?cursor= for the next page; it is null on the last page.

Safe retries

Add an Idempotency-Key header (any unique value, such as a UUID or your deploy id) to POST requests. If a request times out and you send it again with the same key and body within 24 hours, you get the first answer back (with Idempotency-Replayed: true) instead of a second link or a second QA report. Reusing a key for a different request answers 422 with IDEMPOTENCY_KEY_REUSED. Staging secrets (a password or header / cookie value) are not part of this comparison and are never stored with the key: a retry that changes only the secret gets the first answer back.

GitHub Action

The DNSPeek Preview action creates or updates a preview for every pull request, can run a QA report and fail the build on a bad verdict, and comments the preview link and results on the pull request.

.github/workflows/preview.yml
- uses: dnspeek/preview-action@v1
  with:
    api-key: ${{ secrets.DNSPEEK_API_KEY }}
    domain: shop.example.com
    target-ip: 203.0.113.10
    alias: shop-pr-${{ github.event.pull_request.number }}
    run-qa: true
    fail-on-verdict: not_ready
    github-token: ${{ secrets.GITHUB_TOKEN }}

It outputs preview-url, qa-report-url, qa-score and verdict for later steps.

Errors and limits

Every error has the same shape, so your code can branch on error.code:

Error
{ "error": { "code": "LINK_NOT_FOUND", "message": "No link with alias \"shop-preview\" in this workspace" } }
StatusCodeMeaning
400VALIDATION_FAILEDAn invalid or unknown field; error.details.errors lists the problems.
400BAD_REQUEST or POLICY_…A private IP, a blocked target, or a team policy (the message names the rule).
401UNAUTHORIZEDMissing or invalid key (a rotated key’s old secret too), or its creator is no longer a member of the workspace.
401API_KEY_EXPIREDThe key has expired.
402PLAN_REQUIREDThe workspace plan does not include API access.
402QA_QUOTA_EXCEEDEDThis month’s QA reports are used up.
403API_KEY_SCOPEThe key is read only and the request would change data.
403API_KEY_IP_NOT_ALLOWEDThe request came from an address outside the key’s allowed IPs.
403IP_NOT_ALLOWEDYour personal IP allowlist or your team’s Network access refused the address.
404LINK_NOT_FOUND, QA_REPORT_NOT_FOUNDNothing with that alias or id in the key’s workspace.
409CONFLICTThe alias is already taken.
423WORKSPACE_SUSPENDEDThe workspace is suspended.
429RATE_LIMITEDToo many requests for this key and endpoint (the limit comes with your plan; DNS checks: 10 a minute). Wait for Retry-After seconds.

The rate limit comes with your plan: requests per minute that each API key may send to one endpoint. DNS propagation checks stay at 10 per minute on every plan.

PlanRequests per minute per key
Pro60
Agency300
Provider1,000

Each response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so a script can slow down before it hits the limit. The 429 message names your limit and plan, and says which plan allows more when there is one.

Need events pushed to you instead of polling? Use a signed webhook channel. Creating many links without code? Try the CSV import.