API Reference
API Reference
Access BuoyForms programmatically with our REST API.
Authentication
All API requests require an API key passed in the Authorization header:
Authorization: Bearer YOUR_API_KEYCreate API keys in Settings > API Keys.
Base URL
https://buoyforms.com/api/v1The OpenAPI spec is served at /openapi.yaml.
Rate Limits
Rate limits vary by plan and are enforced per API key:
| Plan | Requests/Minute |
|---|---|
| Free | 10 |
| Pro | 100 |
| Business | 500 |
| Enterprise | 2,000 |
Rate Limit Headers
Every API response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per window |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when limit resets |
Handling Rate Limits
When rate limited, the API returns HTTP 429 with a Retry-After header indicating seconds to wait.
{
"error": "rate_limit_exceeded",
"message": "Too many requests",
"retryAfter": 45
}Best practices:
- Implement exponential backoff on 429 responses
- Cache responses where possible
- Batch operations when supported
- Monitor your
X-RateLimit-Remainingheader
Endpoints
Forms
List Forms
GET /formsReturns up to 100 non-deleted forms scoped to the API key's organization, ordered by updatedAt desc.
Response:
{
"forms": [
{
"id": "form_abc123",
"title": "Contact Form",
"description": "",
"status": "published",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-20T14:30:00Z"
}
]
}Get Form
GET /forms/:formIdReturns the form with its full structure (pages, fields, settings).
Create Form
POST /formsBody:
{
"title": "Survey Form",
"description": "Customer feedback survey",
"slug": "survey",
"status": "draft",
"pages": [],
"fields": [],
"settings": {}
}Only title is required. If pages and fields are provided, the form structure is created atomically.
Update Form
PUT /forms/:formIdUpdates form metadata (title, description, slug, status). For structural changes (pages, fields, settings), use the structure endpoint below.
Replace Form Structure
PUT /forms/:formId/structureAtomically replaces the form's pages, fields, and settings.
Body:
{
"pages": [...],
"fields": [...],
"settings": {...}
}Delete Form
DELETE /forms/:formIdSoft-deletes the form.
Submissions
List Submissions
GET /forms/:formId/submissionsQuery Parameters:
limit(integer, default 50): Results per pageoffset(integer, default 0): Pagination offsetstartDate(ISO date): Filter by submission dateendDate(ISO date): Filter by submission date
Get Submission
GET /submissions/:submissionIdCreate Submission
POST /forms/:formId/submissionsUsed for authenticated programmatic form submissions. This endpoint is authenticated with API keys and does not use Turnstile/CAPTCHA verification.
If you are submitting through a published public form or embed, bot protection is controlled by the form's botProtection settings. When bot protection is enabled, the public submission request must include the CAPTCHA token collected by the public form runtime.
Body:
{
"data": {
"fld_email01": "user@example.com",
"fld_message01": "Hello!"
},
"submitterEmail": "user@example.com",
"submitterName": "Jane Example"
}Delete Submission
DELETE /submissions/:submissionIdFields: there is no separate
/fieldsCRUD in v1. Manage fields through thePUT /forms/:formId/structureendpoint, which replaces the pages/fields/settings atomically.
Webhooks
List Webhooks
GET /webhooksList accepts an optional ?formId= query parameter to filter by form.
Create Webhook
POST /webhooksBody:
{
"name": "My webhook",
"url": "https://your-app.com/webhook",
"events": ["submission.created"],
"formId": "form_abc123",
"enabled": true
}formId is optional — omit it for an organization-wide webhook. name, url, and events are required.
Update Webhook
PUT /webhooks/:webhookIdDelete Webhook
DELETE /webhooks/:webhookIdError Responses
Errors return appropriate HTTP status codes with JSON body:
{
"error": "validation_error",
"message": "Invalid request body",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}| Status | Description |
|---|---|
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing API key |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |
Webhooks Signature Verification
Verify webhook authenticity using the X-Signature header:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}SDK Support
Official SDKs coming soon:
- JavaScript/TypeScript
- Python
- Ruby
- Go
Changelog
- 2025-11-27: Added rate limiting headers and documentation
- 2025-11-20: Initial API release