Developers: the Site Complete API

Version 1. Show a park's live availability and waitlist on its own website.

The API lets a park put its live availability on its own website and take waitlist sign-ups, with the same rules as the Site Complete booking page. It never returns personal data: no guest names, emails, phone numbers or stay dates. A site is simply open or not open for the dates you ask about, and a waitlist is a count.

1. Get a key

The park owner creates a key in Marketplace → API Keys → Create key. The full key is shown once; copy it then. A key answers for one park and can be revoked there at any time.

Keys start with sc_pk_ and are publishable: they can only read what the public booking page already shows and add a guest to a waitlist, so it is fine to use one in your website's JavaScript.

2. Make a request

Base URL: https://getsitecomplete.com/api/v1. Send the key in a header:

Authorization: Bearer sc_pk_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

GET /v1/availability

Which sites are open for a date range, by type, with prices and waitlist counts.

GET /v1/sites

The park’s sites: number, type and price. No dates.

POST /v1/waitlist

Put a guest on one site’s waitlist, or on the “any open site” list — the same rules as the booking page.

3. Availability

GET /v1/availability?from=YYYY-MM-DD&to=YYYY-MM-DD. from is the arrival day and to the departure day. Leave to out for an open-ended (monthly) stay. A range can be up to 366 days. A site counts as open only if nothing holds it for those dates, it is not under maintenance, and no Airbnb, Vrbo or Booking.com calendar blocks it. These are the same rules the booking page uses. Prices are in cents, and null means the park does not offer that kind of stay.

curl "https://getsitecomplete.com/api/v1/availability?from=2026-11-01&to=2026-11-08" \ -H "Authorization: Bearer $SITE_COMPLETE_KEY"

{ "apiVersion": "v1", "park": { "name": "Morgan Drive", "timezone": "America/New_York" }, "from": "2026-11-01", "to": "2026-11-08", "currency": "USD", "summary": { "totalSites": 28, "openSites": 21, "anySiteWaitlistCount": 3 }, "siteTypes": [ { "type": "rv_pad", "typeLabel": "RV Site", "totalSites": 25, "openSites": 19, "waitlistCount": 4, "anySiteWaitlistCount": 2, "priceFromCents": { "nightly": null, "weekly": null, "monthly": 100000 } }, { "type": "cabin", "typeLabel": "Cabin", "totalSites": 3, "openSites": 2, "waitlistCount": 0, "anySiteWaitlistCount": 0, "priceFromCents": { "nightly": null, "weekly": null, "monthly": 125000 } } ], "sites": [ { "siteId": "s1", "siteNumber": "1", "name": null, "type": "rv_pad", "typeLabel": "RV Site", "maxRigLengthFt": 45, "open": true, "waitlistCount": 0, "priceCents": { "nightly": null, "weekly": null, "monthly": 100000 } } ] }

4. Sites

GET /v1/sites returns the same sites entries without open or waitlistCount. Use a siteId from here to join a waitlist.

5. Waitlist

POST /v1/waitlist with a JSON body. The guest must agree to be emailed: the offer arrives by email. They get the same confirmation email as a sign-up on the booking page. The answer is the same whether or not that email address was already on the list.

fetch("https://getsitecomplete.com/api/v1/waitlist", { method: "POST", headers: { "Authorization": "Bearer " + KEY, "Content-Type": "application/json" }, body: JSON.stringify({ siteId: "s1", name: "Robin Rig", email: "robin@example.com", phone: "5555550100", // optional; required if consent.sms is true desiredStart: "2026-11-01", stayType: "monthly", // nightly | weekly | monthly consent: { email: true, sms: false } }) }) // 202 → { "apiVersion": "v1", "received": true, "siteId": "s1", "anySite": false, // "message": "If the details are valid, the guest is on the waitlist and has been sent a confirmation email." }

Any open site. Leave out siteId and the guest joins the park's “any open site” list instead: the park offers them whichever site opens up for their dates and fits their rig. Send their dates (desiredEnd is optional) and what they need in rig; anything left out is not a requirement. In /v1/availability, summary.anySiteWaitlistCount counts this list, and each site type'swaitlistCount includes the guests who asked for that type (anySiteWaitlistCount).

body: JSON.stringify({ name: "Robin Rig", email: "robin@example.com", desiredStart: "2026-11-01", desiredEnd: "2026-11-20", stayType: "nightly", rig: { siteType: "rv_pad", rigLengthFt: 38, ampService: "50", water: true, sewer: true }, // all optional consent: { email: true, sms: false } }) // 202 → { "apiVersion": "v1", "received": true, "siteId": null, "anySite": true, "message": "…" }

6. Errors

Every error has the same shape:

{ "error": { "code": "bad_request", "message": "`from` must be a date as YYYY-MM-DD.", "details": { "field": "from" } } }

bad_request

400

A field is missing or could not be read. `details.field` or `details.fields` names it.

missing_key

401

No API key was sent.

invalid_key

The key is not one of ours, or is mistyped.

revoked_key

403

The key was revoked in Marketplace → API Keys. Create a new one.

not_found

404

No such endpoint or version, or the park no longer exists.

method_not_allowed

405

The endpoint exists but takes a different method.

waitlist_closed

409

The park is not taking waitlist sign-ups.

rate_limited

429

Too many requests. Wait a minute and try again.

internal

500

Something went wrong on our side. Try again in a minute.

7. Limits and versions

120 requests a minute and 3,000 an hour per key; 60 a minute per visitor address; 10 waitlist sign-ups an hour per address. Requests from a browser on any website are allowed (CORS).

Paths carry the version (/v1/). A change that could break your code will come as /v2/, and v1 will keep working alongside it. New fields may be added to v1 responses, so ignore any field you do not use.

Questions: support@getsitecomplete.com. See also our Privacy Policy.