Search Records
curl --request POST \
--url https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search \
--header 'Content-Type: application/json' \
--header 'Softr-Api-Key: <api-key>' \
--data '
{
"filter": {
"condition": {
"leftSide": "<string>",
"rightSide": "<string>",
"lowerBound": "<string>",
"upperBound": "<string>",
"conditions": "<array>"
}
},
"sort": [
{
"sortingField": "<string>"
}
],
"paging": {
"offset": 0,
"limit": 10
}
}
'import requests
url = "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search"
payload = {
"filter": { "condition": {
"leftSide": "<string>",
"rightSide": "<string>",
"lowerBound": "<string>",
"upperBound": "<string>",
"conditions": "<array>"
} },
"sort": [{ "sortingField": "<string>" }],
"paging": {
"offset": 0,
"limit": 10
}
}
headers = {
"Softr-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Softr-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: {
condition: {
leftSide: '<string>',
rightSide: '<string>',
lowerBound: '<string>',
upperBound: '<string>',
conditions: '<array>'
}
},
sort: [{sortingField: '<string>'}],
paging: {offset: 0, limit: 10}
})
};
fetch('https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'filter' => [
'condition' => [
'leftSide' => '<string>',
'rightSide' => '<string>',
'lowerBound' => '<string>',
'upperBound' => '<string>',
'conditions' => '<array>'
]
],
'sort' => [
[
'sortingField' => '<string>'
]
],
'paging' => [
'offset' => 0,
'limit' => 10
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Softr-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search"
payload := strings.NewReader("{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Softr-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search")
.header("Softr-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Softr-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "<string>",
"tableId": "<string>",
"fields": {},
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"metadata": {
"offset": 123,
"limit": 123,
"total": 123
}
}Records
Search Records
Search for records in the specified table based on filter, sort, and pagination criteria.
POST
/
databases
/
{databaseId}
/
tables
/
{tableId}
/
records
/
search
Search Records
curl --request POST \
--url https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search \
--header 'Content-Type: application/json' \
--header 'Softr-Api-Key: <api-key>' \
--data '
{
"filter": {
"condition": {
"leftSide": "<string>",
"rightSide": "<string>",
"lowerBound": "<string>",
"upperBound": "<string>",
"conditions": "<array>"
}
},
"sort": [
{
"sortingField": "<string>"
}
],
"paging": {
"offset": 0,
"limit": 10
}
}
'import requests
url = "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search"
payload = {
"filter": { "condition": {
"leftSide": "<string>",
"rightSide": "<string>",
"lowerBound": "<string>",
"upperBound": "<string>",
"conditions": "<array>"
} },
"sort": [{ "sortingField": "<string>" }],
"paging": {
"offset": 0,
"limit": 10
}
}
headers = {
"Softr-Api-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Softr-Api-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
filter: {
condition: {
leftSide: '<string>',
rightSide: '<string>',
lowerBound: '<string>',
upperBound: '<string>',
conditions: '<array>'
}
},
sort: [{sortingField: '<string>'}],
paging: {offset: 0, limit: 10}
})
};
fetch('https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'filter' => [
'condition' => [
'leftSide' => '<string>',
'rightSide' => '<string>',
'lowerBound' => '<string>',
'upperBound' => '<string>',
'conditions' => '<array>'
]
],
'sort' => [
[
'sortingField' => '<string>'
]
],
'paging' => [
'offset' => 0,
'limit' => 10
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Softr-Api-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search"
payload := strings.NewReader("{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Softr-Api-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search")
.header("Softr-Api-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://tables-api.softr.io/api/v1/databases/{databaseId}/tables/{tableId}/records/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Softr-Api-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"filter\": {\n \"condition\": {\n \"leftSide\": \"<string>\",\n \"rightSide\": \"<string>\",\n \"lowerBound\": \"<string>\",\n \"upperBound\": \"<string>\",\n \"conditions\": \"<array>\"\n }\n },\n \"sort\": [\n {\n \"sortingField\": \"<string>\"\n }\n ],\n \"paging\": {\n \"offset\": 0,\n \"limit\": 10\n }\n}"
response = http.request(request)
puts response.read_body{
"data": [
{
"id": "<string>",
"tableId": "<string>",
"fields": {},
"createdAt": "2023-11-07T05:31:56Z",
"updatedAt": "2023-11-07T05:31:56Z"
}
],
"metadata": {
"offset": 123,
"limit": 123,
"total": 123
}
}Filtering Guide
The Search Records endpoint lets you filter, sort, and paginate records using a JSON request body. All three top-level fields (filter, sorting, paging) are optional.
{
"paging": {
"offset": 0,
"limit": 50
},
"sorting": [
{ "sortingField": "field-uuid", "sortType": "ASC" }
],
"filter": {
"condition": { ... }
}
}
Filter Condition Types
Filters are passed infilter.condition. There are four types, determined by the operator.
Binary Conditions
Compare a field to a value:{
"operator": "CONTAINS",
"leftSide": "field-uuid",
"rightSide": "search term"
}
| Operator | Description | Right Side |
|---|---|---|
IS | Equals | single value |
IS_NOT | Not equals | single value |
GREATER_THAN | Greater than | number or date |
GREATER_THAN_OR_EQUALS | Greater than or equal | number or date |
LESS_THAN | Less than | number or date |
LESS_THAN_OR_EQUALS | Less than or equal | number or date |
CONTAINS | String contains | string |
DOES_NOT_CONTAIN | String does not contain | string |
STARTS_WITH | String starts with | string |
DOES_NOT_START_WITH | String does not start with | string |
ENDS_WITH | String ends with | string |
DOES_NOT_END_WITH | String does not end with | string |
IS_ONE_OF | Value is one of | array |
IS_NOT_ONE_OF | Value is none of | array |
HAS_ANY_OF | Multi-value field has any of | array |
HAS_ALL_OF | Multi-value field has all of | array |
HAS_NONE_OF | Multi-value field has none of | array |
Unary Conditions
Check whether a field is empty — norightSide needed:
{
"operator": "IS_EMPTY",
"leftSide": "field-uuid"
}
| Operator | Description |
|---|---|
IS_EMPTY | Field has no value |
IS_NOT_EMPTY | Field has a value |
Ternary Conditions
Range-based checks withlowerBound and upperBound:
{
"operator": "IS_BETWEEN",
"leftSide": "field-uuid",
"lowerBound": 10,
"upperBound": 100
}
| Operator | Description |
|---|---|
IS_BETWEEN | Value is between bounds (inclusive) |
IS_NOT_BETWEEN | Value is outside bounds |
IS_WITHIN | Date is within a relative range |
IS_NOT_WITHIN | Date is outside a relative range |
Composite Conditions
Combine multiple conditions withAND or OR:
{
"operator": "AND",
"conditions": [
{ "operator": "CONTAINS", "leftSide": "name-field", "rightSide": "acme" },
{ "operator": "GREATER_THAN", "leftSide": "amount-field", "rightSide": 1000 },
{
"operator": "OR",
"conditions": [
{ "operator": "IS", "leftSide": "status-field", "rightSide": "active" },
{ "operator": "IS_EMPTY", "leftSide": "archived-field" }
]
}
]
}
Relative Date Values
For date fields, you can use relative date strings instead of fixed ISO 8601 dates:| Format | Examples |
|---|---|
PREDEFINED:<token> | PREDEFINED:TODAY, PREDEFINED:YESTERDAY, PREDEFINED:TOMORROW, PREDEFINED:THIS_WEEK, PREDEFINED:LAST_WEEK, PREDEFINED:THIS_MONTH, PREDEFINED:THIS_YEAR, PREDEFINED:NEXT_MONTH, PREDEFINED:PAST_MONTH, PREDEFINED:7_DAYS_AGO, PREDEFINED:30_DAYS_FROM_NOW |
RELATIVE_DATE:<period>:<unit> | RELATIVE_DATE:THIS:WEEK, RELATIVE_DATE:PAST:MONTH, RELATIVE_DATE:NEXT:QUARTER, RELATIVE_DATE:PAST:7 (7 days ago) |
| ISO 8601 | "2025-05-21", "2025-05-21T20:00:00.000Z" |
Complete Example
Find active contacts named “acme” with an amount over 1000, created this month:{
"paging": { "offset": 0, "limit": 50 },
"sorting": [
{ "sortingField": "amount-field-uuid", "sortType": "DESC" }
],
"filter": {
"condition": {
"operator": "AND",
"conditions": [
{ "operator": "CONTAINS", "leftSide": "name-field-uuid", "rightSide": "acme" },
{ "operator": "GREATER_THAN", "leftSide": "amount-field-uuid", "rightSide": 1000 },
{ "operator": "IS", "leftSide": "status-field-uuid", "rightSide": "active" },
{ "operator": "IS_WITHIN", "leftSide": "created-at-field-uuid", "lowerBound": "RELATIVE_DATE:THIS:MONTH", "upperBound": "PREDEFINED:TODAY" }
]
}
}
}
Authorizations
Query Parameters
If true, use field names as keys in the fields object instead of field IDs.
Body
application/json
Was this page helpful?
⌘I