Developers

REST API

API keys, every endpoint, pagination and errors.

The REST API reads and writes your forms' responses from your own code. Every request is JSON over HTTPS, and every response is JSON.

Base address: https://datamold.crawlink.com/api/v1

API keys

Create a key under Settings → API keys (Admin or Owner). Give it a name, the scopes it needs, optionally the forms it may touch, and optionally an expiry (30, 90 or 365 days).

  • A key looks like fk_… and is shown once. DataMold keeps only a fingerprint of it, so store it somewhere safe; a lost key is replaced, not recovered.
  • Revoke a key at any time; it stops working at once.
  • Each key counts the requests it makes, so you can see which integration is doing what.

Send the key with every request, in either header:

HTTP
Authorization: Bearer fk_YOUR_KEY
X-Api-Key: fk_YOUR_KEY

Scopes

ScopeAllows
forms.readListing forms and reading their fields; usage.
data.readReading responses.
data.writeAdding and changing responses.
data.deleteDeleting responses.

Endpoints

{id} is the form's id (from GET /api/v1/forms), not its public slug.

MethodPathScopeDoes
GET/api/v1/formsforms.readYour forms, with response counts.
GET/api/v1/forms/{id}forms.readOne form and its answer fields.
GET/api/v1/forms/{id}/submissionsdata.readA page of responses.
POST/api/v1/forms/{id}/submissionsdata.writeAdd a response.
GET/api/v1/forms/{id}/submissions/{sid}data.readOne response, with its files.
PATCH/api/v1/forms/{id}/submissions/{sid}data.writeChange some answers.
DELETE/api/v1/forms/{id}/submissions/{sid}data.deleteDelete a response.
GET/api/v1/usageforms.readThis month’s usage against the plan.
GET/api/v1/health—Is the service up.
List your forms
curl "https://datamold.crawlink.com/api/v1/forms" \
  -H "Authorization: Bearer fk_YOUR_KEY"

Listing responses

Query parameters for GET /api/v1/forms/{id}/submissions:

ParameterMeaning
page, pageSizePage number and size (default 50, at most 200).
qFree-text search across the answers and the reference code.
stage, tagOnly responses in this stage, or with this tag.
from, toArrived between these times (epoch milliseconds).
sortBy, sortDirSort by a field key; asc or desc (the default).
whereA condition, field:operator:value. Repeat it for more; between takes low,high.
matchall (default) or any of the conditions.

Operators: eq, neq, contains, notContains, startsWith, endsWith, gt, gte, lt, lte, between, in, isEmpty, isNotEmpty.

Shell
curl -G "https://datamold.crawlink.com/api/v1/forms/FORM_ID/submissions" \
  -H "Authorization: Bearer fk_YOUR_KEY" \
  --data-urlencode "pageSize=100" \
  --data-urlencode "where=city:eq:Pune" \
  --data-urlencode "where=seats:gte:2"
Response
{
  "form": { "id": "01J…", "name": "Event registration", "slug": "k7q2m9xa3b" },
  "submissions": [
    {
      "id": "01J…",
      "code": "K7Q2M",
      "status": "complete",
      "source": "web",
      "data": { "full_name": "Asha Rao", "city": "Pune", "seats": 2 },
      "createdAt": 1791965028499,
      "updatedAt": 1791965028499
    }
  ],
  "page": 1,
  "pageSize": 100,
  "total": 248,
  "nextPage": "https://…/api/v1/forms/01J…/submissions?page=2&pageSize=100"
}

Follow nextPage until it is null to read every page. Each response returned counts as one row view against your plan; reading a single response by its id does not.

Adding and changing responses

Send the answers as a JSON object of field keys and values — flat, or wrapped as {"data": {…}}. They are checked against the form's rules exactly as a responder's would be. A new response answers 201 with the response; PATCH changes only the keys you send.

Shell
curl -X POST "https://datamold.crawlink.com/api/v1/forms/FORM_ID/submissions" \
  -H "Authorization: Bearer fk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"full_name": "Asha Rao", "city": "Pune", "seats": 2}'

Submitting without a key

Each form also has a public endpoint for your own forms and front ends. It needs no key and takes the form's slug. It obeys your allowed domains, and one address can send at most 60 responses a minute.

JavaScript
fetch('https://datamold.crawlink.com/api/public/forms/YOUR_FORM_SLUG/submissions', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ full_name: 'Asha Rao', email: 'asha@example.com' })
});
  • 201 answers { "id", "code", "createdAt" } — the code is the reference to show the person.
  • JSON and ordinary form posts (application/x-www-form-urlencoded) both work.
  • GET /api/public/forms/YOUR_FORM_SLUG returns the form's public definition, if you want to build the form from it.
  • Files: post each file as multipart/form-data (field file) to /api/public/uploads?form=SLUG&field=KEY, then send the id it returns as that field's answer. 25 MB per file at most; uploads need a paid plan.

Errors

A failed request answers with a status and a message a person can read:

422 response
{
  "message": "Some answers need fixing.",
  "errors": [{ "key": "email", "message": "…what is wrong with this answer…" }]
}
StatusMeans
401No key, an unknown key, or a key that was revoked or has expired.
403The key lacks the scope, or is not allowed this form.
402A plan limit is used up for the month (responses or row views).
404No such form or response.
409The form is not accepting responses, or a “unique” answer was already given.
422Some answers are invalid; `errors` lists them by field key.
429Too many submissions from one address (public endpoint only).

Limits

The API has no request-rate limit of its own; it shares your plan's monthly meters. Listing and exporting count row views, and adding responses counts responses. Check where you stand with GET /api/v1/usage. See how usage is counted.