API
Everything the dashboard shows for a property is readable over HTTPS, and the things your backend knows (an order, a signup, a campaign) are writable back onto the sessions that produced them. One token, one property, JSON in and out.
curl https://api.snipform.io/v2/property/overview \ -H "Authorization: Bearer YOUR_TOKEN"{ "code": 200, "status": "OK", "data": { "id": "64f1c2a9e4b0a1b2c3d4e5f6", "name": "acme.com", "domain": "acme.com", "has_signals": true, "state": "active", "state_name": "Active", "counts": { "sessions": 48211, "forms": 3, "pages": 1 } }}Base URL
Section titled “Base URL”All authenticated endpoints live under:
https://api.snipform.io/v2The token identifies the property, so no property id appears in any path. Every route is /v2/property/....
Authentication
Section titled “Authentication”Send a property API token as a bearer token on every request:
Authorization: Bearer YOUR_TOKENTokens are created per property in the dashboard, carry a set of scopes, and are shown once. See Authentication & Tokens for creation, expiry, revocation and the difference between API and MCP tokens.
Request format
Section titled “Request format”- Send JSON bodies with
Content-Type: application/json.GETendpoints take query-string parameters. - The
Acceptheader is forced toapplication/jsonserver-side; you always get JSON back. - Money is always an integer in minor units (cents). Timestamps ending in
_tsare unix seconds, UTC. - Dates in request bodies are
YYYY-MM-DDand are interpreted in UTC.
Response envelope
Section titled “Response envelope”Every response, success or error, is wrapped the same way:
{ "code": 200, "status": "OK", "data": { }}| Key | Meaning |
|---|---|
code | The HTTP status code, repeated in the body |
status | The HTTP reason phrase (OK, Forbidden, Not Found, …) |
data | The endpoint’s payload. On errors, the error object described below |
All response shapes documented in this section are the contents of data.
Errors
Section titled “Errors”Errors carry a human-readable message and a stable error_code you can branch on:
{ "code": 403, "status": "Forbidden", "data": { "message": "Missing scope: signals:read", "error_code": "forbidden" }}| HTTP | error_code | When |
|---|---|---|
| 401 | unauthenticated | No token, an invalid token, or a revoked/expired token |
| 403 | forbidden | The token lacks a required scope (Missing scope: ...), or is not bound to a property |
| 404 | not_found | The record is not on this property, the token’s property was deleted, or the property has no data yet |
| 410 | error | The record was deleted and cannot be changed (contacts) |
| 422 | validation_failed | A request field failed validation. errors maps field names to messages |
| 500 | server_error | Something broke on our side |
Validation failures use Laravel’s field map:
{ "code": 422, "status": "Unprocessable Content", "data": { "message": "The name field is required.", "errors": { "name": ["The name field is required."] } }}Analytics endpoints return 404 with This property has no data until the tracker has recorded its first session.
Pagination
Section titled “Pagination”Two shapes, depending on the endpoint:
Page-numbered (sessions, links, clicks, contacts, conversion sessions): Laravel’s paginator. Pass ?page=N; the size key is per_page on GET endpoints and limit on POST /v2/property/signals/sessions.
{ "data": [ ], "current_page": 1, "last_page": 12, "per_page": 25, "total": 289, "from": 1, "to": 25, "next_page_url": "https://api.snipform.io/v2/property/links?page=2", "prev_page_url": null}Cursor (a contact’s sessions): newest first, keyed on the session entry time.
{ "sessions": [ ], "paging": { "per_page": 25, "has_more": true, "next_cursor": 1755700000 }}Scopes
Section titled “Scopes”A token carries the scopes you picked when you created it. Each endpoint names the scope it checks; a token without it gets 403 Missing scope: <scope>.
| Scope | Grants |
|---|---|
signals:read | Analytics, sessions feed, attribution preview and presets |
signals:write | Record events and acquisition data on sessions |
conversions:read | Conversion definitions, summaries, funnels, segments, cycles |
conversions:write | Create, update, publish, pause and delete conversions |
shortlinks:read | Link groups, links and clicks |
shortlinks:write | Create, update and delete groups and links |
contacts:read | Identified contacts and their sessions |
contacts:write | Identify, update and delete contacts |
forms:read | Declared for forward compatibility. No endpoint consumes it yet |
mcp:use | MCP tokens only. Grants the MCP server, never these REST endpoints |
GET /v2/property/overview needs no scope at all, which makes it a cheap connectivity check for any token.
Rate limits
Section titled “Rate limits”The authenticated REST API has no published per-token limit today. The MCP server is throttled to 60 requests per minute per client and the public tracker endpoints to 120 per minute per IP. Analytics endpoints run Elasticsearch aggregations on every call; cache on your side rather than polling.
Endpoints
Section titled “Endpoints”- Property - one call that confirms the token works and names the property it belongs to.
- Signals analytics - metrics, time series, live view and the paginated sessions feed, all filterable.
- Query language - the clause format every analytics endpoint accepts.
- Field catalog - every filterable session field, the operators it takes, periods and metrics.
- Session actions - record events and revenue on a session from your backend.
- Contacts - identify people, read their sessions, update or delete them.
- Conversions - define funnels and read their results.
- Short links - groups, links with UTMs, and click logs.
- Attribution - preview how a URL will be classified before you ship it.
- Public endpoints - what the tracker and form libraries call. No token, signed requests.
Versioning
Section titled “Versioning”/v2 is the current and only supported version. The older /core/* form endpoints still answer for existing embeds but are not for new integrations. Additive changes (new fields, new endpoints) ship without a version bump; anything that would break a documented shape gets a new prefix.