Websites API
The Websites API builds websites for local businesses: a full website for one business, or a one-page preview site for every business in a list or in one of your searches. It also checks builds, changes previews, makes Online Presence Report links and takes your clients' website requests. It runs the same builds as the dashboard and uses the same credits.
Every route is also in the API reference and in the OpenAPI file.
Using an AI agent? The MCP server has the same operations as tools, plus tools to change a finished website, and the websites skill teaches coding agents to use them.
Base URL
Send requests to the same domain you use for your dashboard. Every path on this page is relative to it.
Authentication
Send your API key as a bearer token on every request:
Authorization: Bearer YOUR_API_KEY
You can also send the key in an x-api-key header instead. Both work on every path on this page. To make a key, open API Keys in your dashboard, type a name in the Key label box, and click + Create New Key. The full key is shown once. Treat it like a password.
Client requests use a white-label agency key, which starts with sf_agency_. Everything else uses an account key.
Websites and batches belong to your account: any of your keys can read them, and so can the dashboard.
Plan first, then confirm
A build or a preview batch answers first with a plan: what it will make and what it costs. Nothing starts and nothing is spent. Send the same request again with "confirm": true to start it.
What things cost
| What | Cost |
|---|---|
| A website | 1 website credit. Given back if the build fails. |
| A preview batch | 1 batch site per business, at least 5. Your plan's monthly batch sites are used first, then website credits. Given back only if no preview in the batch gets built. |
| Designs, Google lookup, status, lists, restyling, changing a preview, reports | Free. |
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /api/websites/designs | The designs, with a picture of each. No key needed. |
| GET | /api/websites/business-lookup | Find a business on Google, then get its details. |
| POST | /api/websites | Plan or build a website. |
| GET | /api/websites | Your websites, newest first. |
| GET | /api/websites/{websiteId} | A build's progress and preview link. |
| POST | /api/websites/batches | Plan or start a preview batch. |
| GET | /api/websites/batches | Your preview batches, newest first. |
| GET | /api/websites/batches/{batchId} | One batch and every preview link. |
| PATCH | /api/websites/batches/{batchId} | Restyle a batch. |
| GET | /api/websites/batches/{batchId}/results | The previews as JSON, a CSV, or format=ghl for a GoHighLevel import with each preview link. |
| PATCH | /api/websites/batches/{batchId}/previews/{previewId} | Change one preview. |
| POST | /api/websites/requests | Save a client's website request (agency key). |
| GET | /api/websites/requests/{requestId} | Check a client's request (agency key). |
| POST | /api/reports | Make an Online Presence Report link. |
Build a website
Only business_name is required. Pick a design from the designs list; left out, the site uses classic.
curl -X POST https://YOUR_DASHBOARD_DOMAIN/api/websites \
-H "Authorization: Bearer $SHOWFIRST_API_KEY" \
-H "content-type: application/json" \
-d '{"business_name": "Harbor Plumbing", "design": "bold", "city": "Sarasota", "state": "FL"}'
The plan:
{
"ok": true,
"requires_confirmation": true,
"plan": { "business_name": "Harbor Plumbing", "design": "bold",
"website_credits_needed": 1, "website_credits_left": 3 },
"next": "Nothing has started. Send the same request with confirm: true to build it."
}
Send the same body with "confirm": true. The answer is 202 with a website_id:
{ "ok": true, "started": true, "website_id": "0c9b8a52-3d0e-4f7e-9d1d-2a6c7e1b5f10",
"status": "queued", "preview_url": null, "website_credits_used": 1, "website_credits_left": 2 }
Fields
| Field | What it is |
|---|---|
business_name | Business name. Required unless from_search is sent. |
design | classic, bold, night, angle or waterline. |
phone, email, website | Contact details and their current website. |
address | Full address, or just a town or service area. Or the parts: street, city, state, postal_code, country_code. |
show_street_address | false hides the street address and postal code. |
industry, about | What the business does, and a few sentences about it. |
services, service_areas | Lists of services and towns served. |
hours | Opening hours as text. |
google_place_id | From the Google lookup. Name, address, phone and hours come from Google. |
brand_color, logo_url | Six-digit hex colour, and a direct link to a logo. |
language | en, es, fr, de, it, ro or tr. |
from_search | {"job_id", "place_id"}: one business from one of your finished searches. Its details fill the build; anything you also send wins. |
Check a build
curl https://YOUR_DASHBOARD_DOMAIN/api/websites/WEBSITE_ID \
-H "Authorization: Bearer $SHOWFIRST_API_KEY"
Check every 15 to 30 seconds. status goes queued, building, preview_ready (the preview_url works from here), finishing and done. failed means it did not build and the credit came back. A build takes several minutes.
Find a business on Google
curl "https://YOUR_DASHBOARD_DOMAIN/api/websites/business-lookup?name=Harbor%20Plumbing&city=Sarasota" \
-H "Authorization: Bearer $SHOWFIRST_API_KEY"
The answer lists matches and a lookup_session. Call again with the chosen place_id and the same lookup_session for the business's address, phone, hours and website, then build with google_place_id.
Preview batches
A preview batch makes a one-page preview site for every business, up to 500, to pitch many leads at once. Send businesses, or from_search with a finished search's job_id:
curl -X POST https://YOUR_DASHBOARD_DOMAIN/api/websites/batches \
-H "Authorization: Bearer $SHOWFIRST_API_KEY" \
-H "content-type: application/json" \
-d '{"industry": "Plumbing", "design": "night", "from_search": {"job_id": "JOB_ID", "has_website": false}}'
from_search also takes has_email, has_phone and max_businesses. Each business in businesses takes business_name, google_place_id or google_cid, phone, email, address, website, hours, brand_color and logo_url. The whole batch takes industry, design, brand_color, background (light or dark), logo_size (48 to 160) and language.
The plan shows businesses, batch_sites_from_plan and website_credits_needed. Send the same body with "confirm": true to start; the answer has the batch_id.
GET /api/websites/batches/{batchId} lists every preview with its preview_url. Add /results?format=csv for the spreadsheet the dashboard's Export button gives.
curl -X PATCH https://YOUR_DASHBOARD_DOMAIN/api/websites/batches/BATCH_ID/previews/PREVIEW_ID \
-H "Authorization: Bearer $SHOWFIRST_API_KEY" \
-H "content-type: application/json" \
-d '{"phone": "(941) 555-0102"}'
That changes one preview. null clears a field. To restyle the whole batch, PATCH /api/websites/batches/{batchId} with brand_color, background, logo_size or (before it is built) language; previews with their own colour or background keep it.
Online Presence Reports
A report shows a business owner how they show up online next to their top local competitors. Make one for any business from one of your finished searches:
curl -X POST https://YOUR_DASHBOARD_DOMAIN/api/reports \
-H "Authorization: Bearer $SHOWFIRST_API_KEY" \
-H "content-type: application/json" \
-d '{"job_id": "JOB_ID", "place_id": "PLACE_ID", "website_url": "https://your-preview.example/"}'
The answer's report_url is a page you can send. It shows public facts only, never the owner's contact details. website_url is optional: a website you built for them, shown on the report. Asking again for the same business gives the same link; "new_link": true makes a new one. Free.
Client requests
For agencies that take website requests from their own clients. With an agency key, POST /api/websites/requests takes the same fields as a build, plus client_email (required; saved as the business email, we never write to it) and build_now. Without build_now, the request waits for Approve on your API Keys page, and the website credit is used then. GET /api/websites/requests/{requestId} shows the approval and the build's progress.
Errors
Errors have "ok": false and an error. Validation errors also have details, one line per problem, by field name.
| Status | Meaning |
|---|---|
| 400 | Something in the request is wrong. Nothing was started or spent. |
| 401 | The key is missing, unknown or revoked. |
| 403 | Not enough credits, your plan has no batch sites, or the key is not linked to an account. Nothing was spent. |
| 404 | Unknown ID, or it belongs to another account. |
| 409 | A batch's language can only change before it is built. |
| 503 | Building is under maintenance, or Google lookup is not available. Nothing was spent. |
A build can also answer "paused": true: builds are paused for now. Nothing was queued or spent; try again later.