Query and export entries
Filter a form's entries with query, keep responses small with fields, page through large results, and export entries to CSV, Excel or PDF.
This page shows how to read many entries at once. You will filter a form's entries with query,
trim each item with fields, page through a large result, and export entries to a CSV, Excel or
PDF file.
List a form's entries
GET /entry lists entries. It takes four parameters in the
query string:
| Parameter | What it does |
|---|---|
query | A JSON object that filters the entries. Always include the form. See Filter with query. |
fields | A JSON array of the properties to keep in each item. See Choose the fields. |
limit | The page size, up to 2,000. Always send it. See Page through the results. |
after | The cursor of the next page. Take it from the Link header. |
This lists a form's submitted entries, two per page:
curl -G -i https://api.tepez.co.il/v1.0/entry \
-H "x-api-key: $TEPEZ_API_KEY" \
--data-urlencode 'query={"form":"<formId>","status":2}' \
--data-urlencode 'fields=["id","submitTime","data"]' \
--data-urlencode 'limit=2'The response, with the headers trimmed:
HTTP/1.1 200 OK
link: <https://api.tepez.co.il/v1.0/entry?after=<cursor>&query=…&fields=…&limit=2>; rel=next{
"data": [
{
"id": "<entryId>",
"submitTime": "2026-09-30T19:45:59.995Z",
"data": {
"fullName": "Dana Levi",
"email": "dana@example.com",
"position": "dev",
"address": { "city": "Haifa", "street": "Herzl 10" }
}
},
{
"id": "<entryId2>",
"submitTime": "2026-09-30T19:46:10.168Z",
"data": {
"fullName": "Yossi Cohen",
"email": "yossi.cohen@example.com",
"position": "qa"
}
}
]
}The link header means more entries match. The next section shows how to follow it.
Filter with query
query is a JSON object. Each key names a property of the entry, and takes either a value, which
must match exactly, or an object of operators. The API accepts these keys:
| Key | Type | Operators |
|---|---|---|
form | form id | $eq $ne $in $nin |
_id | entry id | $eq $ne $in $nin |
status | status number, 0 to 5 | $eq $ne $in $nin |
changedAt | date | $eq $ne $in $nin $gt $gte $lt $lte |
modifyTime | date | $eq $ne $in $nin $gt $gte $lt $lte |
deleted | boolean | $eq $ne |
statustakes the number, not the label:2, not"submitted". The numbers are listed in Statuses.- Note
_idwith an underscore. Entries in responses carry the same value asid. deletedadds nothing on a plain list request (withoutdeleteMarkers): such requests never return deleted entries. To learn which entries were deleted, usedeleteMarkers=true, as in Poll for changes.- Other keys, such as
submitTimeordata.*, fail with400, for example"query.submitTime" is not allowed. So does an operator the key doesn't take.
A few filters, each with form as the first key:
{ "form": "<formId>", "status": { "$in": [1, 2] } }Drafts and submitted entries.
{ "form": "<formId>", "modifyTime": { "$gte": "2026-09-01T00:00:00.000Z", "$lt": "2026-10-01T00:00:00.000Z" } }Entries whose data or status changed in September 2026, UTC.
{ "form": "<formId>", "_id": { "$in": ["<entryId>", "<entryId2>"] } }Specific entries, for example the ones your system still waits for.
No filters on the entry's data
query can't filter on the values people filled in. A key such as data.position fails with
400 "query.data.position" is not allowed. Earlier versions of the API accepted such filters. They
were removed in March 2025.
To find entries by a value, list them with a filter the API accepts, such as form, status and
a date, and filter the results in your system. To look entries up by your own id, store the Tepez
entry id next to your record when you create the entry.
Dates
Send dates as ISO 8601 strings, like the ones in responses:
"2026-09-30T19:46:00.000Z"is UTC."2026-09-30T22:46:00+03:00"is the same moment in Israel time."2026-09-30"is midnight UTC at the start of that day.
Other formats, such as 30/09/2026, fail with 400 "query.modifyTime.$gt" must be a valid date.
Responses always give times in UTC.
changedAt or modifyTime
Every entry has two "last changed" times:
| Property | Moves when |
|---|---|
modifyTime | The entry's data or status changes. |
changedAt | Anything about the entry changes, including a PDF archived or an email sent after the submit. |
Use changedAt to catch every change, for example when you sync entries to your system. Use
modifyTime to find entries whose data changed. Get notified when a form is submitted
has a complete polling recipe built on changedAt.
Choose the fields
fields is a JSON array of the properties to keep in each item. Leave it out to get whole entries,
with their actions, emails and PDFs.
- List
idexplicitly.fieldsdrops every property you don't name,idincluded. - A dotted path keeps one value from inside the entry:
["id","data.fullName"]returns{"id": "<entryId>", "data": {"fullName": "Dana Levi"}}. - Send a JSON array. A comma list, such as
fields=id,status, returns200with empty items. - Names that don't exist are ignored without an error.
Page through the results
- Always send
limit. Without it, the API returns every matching entry in one response, which is slow for a large form.limitgoes up to 2,000 (see Limits). - Follow the
Linkheader until a response has none. When more entries match, the response carriesLink: <…>; rel=next, the full URL of the next page, withafterset to an opaque cursor. Use the URL as it is. - Results come in the order the entries were created, oldest first. There is no
sortparameter:GET /entryignores it, and the order doesn't change.
This fetches every submitted entry of a form:
# The next page: the URL from the Link header of the previous response
curl -G -i https://api.tepez.co.il/v1.0/entry \
-H "x-api-key: $TEPEZ_API_KEY" \
--data-urlencode 'query={"form":"<formId>","status":2}' \
--data-urlencode 'fields=["id","submitTime","data"]' \
--data-urlencode 'limit=2' \
--data-urlencode 'after=<cursor>'The last page has no Link header:
HTTP/1.1 200 OK{
"data": [
{
"id": "<entryId5>",
"submitTime": "2026-09-30T19:47:27.114Z",
"data": { "fullName": "Avi Mor", "position": "dev" }
}
]
}Export entries to a file
POST /entry/export writes a form's entries to a file
and returns a link to download it. It is the same export as the Export button in the
management app.
format, in the query string:csv,xlsxorpdf.query, in the body: the same filter as inGET /entry. It must includeform.fields, in the body, optional: the columns to include, in order. Leave it out to get the form's default columns.
curl -X POST "https://api.tepez.co.il/v1.0/entry/export?format=csv" \
-H "x-api-key: $TEPEZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": {"form": "<formId>", "status": 2},
"fields": ["id", "submitTime", "root≡fullName", "root≡address≡city", "root≡position"]
}'On Windows, curl mangles the ≡ in an argument. Put the body in a UTF-8 file and send it with
--data-binary @body.json instead of -d.
{
"url": "https://<bucket>.s3.eu-west-1.amazonaws.com/<uuid>/Job%20application.csv?<presigned-query>"
}Download the file from url. It needs no API key, and it stays valid for about 2 days. CSV and Excel
files are stored compressed, so tell your HTTP client to decompress them: curl needs --compressed, while
browsers, fetch and Python's requests decompress it on their own.
curl --compressed -o entries.csv "<url>"Unique ID,Submitted,Full name,City,Position
<entryId>,09/30/2026 7:45 PM,Dana Levi,Haifa,dev
<entryId2>,09/30/2026 7:46 PM,Yossi Cohen,,qa
<entryId5>,09/30/2026 7:47 PM,Avi Mor,,devWhat each format holds:
format | The file |
|---|---|
csv | One row per entry, UTF-8 with a byte order mark, so Excel shows Hebrew correctly. The first row holds the column titles. |
xlsx | The same rows as an Excel workbook. |
pdf | A ZIP file with one PDF per entry, rendered now from each entry's current data. fields doesn't apply. |
To keep the archived PDFs and the uploaded files instead, see Download PDFs and files.
Column names
Each item of fields is a column's name:
- Entry properties go by their name:
id,idx,submitIdx,status,name,createTime,modifyTime,submitTime,form.id,form.name,resumeUrl,resumeUrlBewit,editUrl,cloneUrl,shortUrl. - A field's column is the
nameof every element on its path, fromrootdown, joined with≡(U+2261):root≡fullName,root≡address≡city. Unlike data paths, the path includes every section and tab, so a file fieldcvinside tabs namedstepsisroot≡steps≡cv.
A name that doesn't exist fails with 400 The following fields are not recognized …, listing it.
The export isn't paged: one file holds every matching entry. Deleted entries are never included.
Troubleshooting
| Response | Cause and fix |
|---|---|
400 "query.data.…" is not allowed | query can't filter on the entry's data. Filter by form, status and a date, then filter the results in your system. |
400 "query.<key>" is not allowed | The key isn't one of the keys in Filter with query. |
400 "query.status" must be one of [object, 0, 1, 2, 3, 4, 5] | query takes the status number, not its label: "status":2, not "status":"submitted". |
400 "query" must be of type object | query isn't valid JSON. Quote the keys and the operators: {"form":"<formId>"}. |
400 "query.modifyTime.$gt" must be a valid date | Send the date in ISO 8601, such as 2026-09-30T00:00:00.000Z. |
400 "limit" must be less than or equal to 2000 | Lower limit, and follow the Link header for the rest. |
200 with empty items ({"data":[{},{}]}) | fields was a comma list. Send a JSON array, and include "id". |
The same entries come back in the same order despite sort | There is no sort. Entries always come oldest first. Sort them in your system. |
| Some entries are missing from a long list | Your code stopped before the last page. Follow Link until a response has none. |
400 Query must contain a form from the export | Put form in the export's query. |
400 The following fields are not recognized … from the export | A column name is wrong. Check the path, including sections and tabs, and the ≡ separator. |
| The downloaded CSV is unreadable binary | Your client saved the compressed file. Download it again with curl --compressed. |
The export link returns 403 AccessDenied | It expired after about 2 days. Export again. |
Errors explains the error format and lists more messages.
Every response from the API carries an x-tepez-request-id header. Quote it when you contact us.
Question about this page? Email support@tepez.co.il