Endpoints
Apps
List All Apps
This endpoint returns all apps accessible to the authenticated user.
https://api.apper.io/v1/meta/apps
Response:
{
"success": true,
"message": null,
"total": 2,
"hasMore": false,
"data": [
{
"appId": "8f505ab65c50482a838f1336511f00eb",
"label": "ShopTrack Pro",
"modifiedOn": "2025-11-13T15:02:00.869443",
"createdOn": "2025-11-13T14:19:23.640768"
},
{
"appId": "cb58e89a7a754ba4b0ad5492dd5499d0",
"label": "DayFlow",
"modifiedOn": "2025-11-10T19:05:42.933493",
"createdOn": "2025-11-10T17:38:43.704853"
}
]
}
Important: Use the appId field for all other API calls.
App Object Properties:
| Field | Type | Description |
|---|---|---|
| appId | string | App identifier (use as appId in other endpoints) |
| label | string | Display name |
| createdOn | datetime | ISO 8601 timestamp |
| modifiedOn | datetime | ISO 8601 timestamp |
Tables
Tables are like spreadsheets in your app - they store the actual data of your app. Every table has:
- Structure: What fields exist (columns)
- Records: The actual data (rows)
List Tables
https://api.apper.io/v1/meta/{appId}/tables
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
| appId | string | App unique identifier |
Response:
{
,
"success": true,
"message": "",
"data": [
{
"id": 1039,
"name": "customer",
"label": "Customer",
"plural": "Customers",
"createdBy": { "id": 2006, "name": "John Doe" },
"createdOn": "2024-04-26T11:43:40",
"modifiedBy": { "id": 2006, "name": "John Doe" },
"modifiedOn": "2024-04-26T11:43:40"
},
{
"id": 1040,
"name": "order",
"label": "Order",
"plural": "Orders",
"createdBy": { "id": 2006, "name": "John Doe" },
"createdOn": "2024-04-26T11:43:40",
"modifiedBy": { "id": 2006, "name": "John Doe" },
"modifiedOn": "2024-04-26T11:43:40"
},
]}
Important: Use the name field (not label) in API calls. So if the table's name is "customer", use that in your URLs.
Fields
Before working with records, you need to know what fields (columns) exist in your table.
List Table Fields
Get the structure/schema of a table.
Why this matters: You need to know field types to format your data correctly when creating/updating records.
https://api.apper.io/v1/meta/{appId}/tables/{tableName}/fields
Response:
{
"success": true,
"message": "",
"data": [
{
"id": 15824,
"name": "Id",
"label": "Id",
"type": "Number",
"isRequired": false,
"isDependentPicklist": false,
"options": null,
"dependentField": null,
"createdBy": {
"id": 2006,
"name": "John Doe"
},
"createdOn": "2024-05-10T11:14:51",
"modifiedBy": {
"id": 2006,
"name": "John Doe"
},
"modifiedOn": "2024-05-10T11:14:51"
},
{
"id": 15825,
"name": "Name",
"label": "Name",
"type": "Text",
"isRequired": true,
"isDependentPicklist": false,
"options": null,
"dependentField": null,
"createdBy": {
"id": 2006,
"name": "John Doe"
},
"createdOn": "2024-05-10T11:14:54",
"modifiedBy": {
"id": 2006,
"name": "John Doe"
},
"modifiedOn": "2024-05-10T11:14:54"
}
]
}
Supported Field Types:
| Type | Description | Value Format |
|---|---|---|
| Number | Numeric values | Integer or decimal |
| Text | Plain text | String |
| Email address | Valid email string | |
| Date | Dates only | "2025-01-15" |
| DateTime | Dates with time | "2025-01-15T14:30:00" |
| Boolean | True/False checkboxes | true or false |
| Picklist | Single-select dropdown | String from options array |
| Multipicklist | Multi-select dropdown | Comma-separated options |
| Lookup | Reference to another table | Record ID (integer) |
| MasterDetail | Parent-child relationship | Record ID (integer) |
Why this matters: You need to know field types to format your data correctly when creating/updating records.
Dependent Picklist Example
When isDependentPicklist is true, the field's available options depend on another field:
{
"name": "State",
"type": "Picklist",
"isDependentPicklist": true,
"dependentField": {
"parentFieldId": 16678,
"parentFieldName": "Country",
"mappings": [
{ "forOption": "India", "options": ["Maharashtra", "Gujarat"] },
{ "forOption": "USA", "options": ["Texas", "California"] }
]
}
}
Records
Apper has two ways to retrieve records. Here's when to use each:
List Records vs Search/Filter Records
Think of it like this:
📋 List Records = "Show me everything" or "Show me until page 2"
- Simple pagination through all records
- Basic sorting (A-Z, newest first, etc.)
- No filtering conditions
🔍 Search/Filter Records = "Show me records that match specific criteria"
- Complex filtering (status = Active, amount > 1000, etc.)
- Combine multiple conditions with AND/OR logic
- When you need to find specific records
List Records
Use this when: You want all records, or simple sorted/paginated access.
https://api.apper.io/v1/data/{appId}/tables/{tableName}
Request Body:
{
"fields": ["Id", "Name", "Email"],
"orderBy": [
{
"fieldName": "Name",
"sortType": "Asc"
}
],
"pagingInfo": {
"limit": 50,
"offset": 0
}
}
Request Body Parameters:
| Field | Type | Description |
|---|---|---|
| fields | array | Which fields to include in response (omit for all fields) |
| orderBy | array | Sort configuration |
| orderBy[].fieldName | string | Field to sort by |
| orderBy[].sortType | string | Asc or Desc |
| pagingInfo.limit | integer | Records per page (default: 20, max: 100) |
| pagingInfo.offset | integer | Number of records to skip (default: 0) |
Response:
{
"success": true,
"message": null,
"total": 150,
"hasMore": true,
"data": [
{
"Id": 1,
"Name": "Alice Johnson",
"Email": "[email protected]"
},
{
"Id": 2,
"Name": "Bob Smith",
"Email": "[email protected]"
}
]
}
Example 1: Get First 20 Customers
{
"fields": ["Id", "Name", "Email"],
"pagingInfo": {
"limit": 20,
"offset": 0
}
}
Example 2: Get Next 20 Customers (Page 2)
{
"fields": ["Id", "Name", "Email"],
"pagingInfo": {
"limit": 20,
"offset": 20
}
}
How pagination works:
- First page:
offset: 0, limit: 20(records 1-20) - Second page:
offset: 20, limit: 20(records 21-40) - Third page:
offset: 40, limit: 20(records 41-60)
Example 3: Sort by Creation Date (Newest First)
{
"fields": ["Id", "Name", "CreatedOn"],
"orderBy": [
{
"fieldName": "CreatedOn",
"sortType": "Desc"
}
],
"pagingInfo": {
"limit": 50
}
}
Search/Filter Records
Use this when: You need to find specific records based on certain conditions.
https://api.apper.io/v1/data/{appId}/tables/{tableName}
Request Body:
{
"fields": ["Id", "Name", "Email", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
}
],
"orderBy": [
{
"fieldName": "Id",
"sortType": "Desc"
}
],
"pagingInfo": {
"limit": 100,
"offset": 0
}
}
Request Body Parameters:
| Field | Type | Description |
|---|---|---|
| fields | array | Field names to return |
| where | array | Simple filter conditions (AND combined) |
| whereGroups | string | Complex grouped filter conditions (AND/OR logic) |
| orderBy | string | Asc or Desc |
| pagingInfo | integer | Max records to return (default: 20) |
| groupBy | array | Fields to group results by |
| aggregators | array | Sub-queries for aggregation (count, sum, etc.) |
Response
{
"success": true,
"message": null,
"total": 15,
"data": [
{
"Id": 45,
"Name": "Sarah Johnson",
"Email": "[email protected]",
"Status": "Active"
},
{
"Id": 32,
"Name": "Mike Chen",
"Email": "[email protected]",
"Status": "Active"
}
]
}
Understanding Filters
Apper's filtering system has three building blocks:
- where = Simple conditions joined with AND
- whereGroups = Complex conditions with AND/OR logic
Let's break each one down.
Building Block 1: Simple Filters with "where"
The where array is for straightforward filtering. All conditions are joined with AND logic.
What this means in plain English:
where: [condition1, condition2, condition3]
This means "Give me records where condition1 AND condition2 AND condition3 are all true"
Basic structure:
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
}
Parameters:
| Parameter | What It Is | Example |
|---|---|---|
| fieldName | Which field to filter | "Status", "Email", "Amount" |
| operator | How to compare | "EqualTo", "Contains", "GreaterThan" |
| values | What to compare against | ["Active"] or ["1000"] |
| include | Include (true) or exclude (false) matches | true (default) |
Common Operators Quick Reference
Supported Operators:
| Operator | What It Does | Example Use Case |
|---|---|---|
| EqualTo | Exact match | Status = "Active" |
| NotEqualTo | Not equal | Status ≠ "Archived" |
| Contains | Contains text | Name contains "Tech" |
| StartsWith | Begins with | Phone starts with "+1" |
| EndsWith | Ends with | Email ends with ".com" |
| GreaterThan | Numeric > | Amount > 1000 |
| LessThan | Numeric < | Stock < 10 |
| GreaterThanOrEqualTo | Numeric >= | Score >= 75 |
| LessThanOrEqualTo | Numeric <= | Age <= 65 |
| Includes | Match any of (OR) | Status in ["Active", "Pending"] |
| Excludes | Match none of | Type not in ["Draft", "Deleted"] |
💡 Pro Tip: For a complete list of operators by field type, check our Filters Reference Guide.
Common Filter Examples:
Example 1: Filter by a single conditionFind all active customers:
{
"fields": ["Id", "Name", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
}
]
}
Find active customers in California:
{
"fields": ["Id", "Name", "State", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
},
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["California"]
}
]
}
Find companies with "Tech" in their name:
{
"fields": ["Id", "Name"],
"where": [
{
"fieldName": "Name",
"operator": "Contains",
"values": ["Tech"]
}
]
}
Find orders over $1000:
{
"fields": ["Id", "OrderNumber", "Amount"],
"where": [
{
"fieldName": "Amount",
"operator": "GreaterThan",
"values": [1000]
}
]
}
Find records created this month:
{
"fields": ["Id", "Name", "CreatedOn"],
"where": [
{
"fieldName": "CreatedOn",
"operator": "RelativeMatch",
"values": ["this month"]
}
]
}
What's RelativeMatch? It's Apper's way of using natural language for dates. Instead of hardcoding "2025-01-01", you can say:
"this month"- Current month (auto-updates every month)"last 30 day"- Last 30 days from today"next week"- Next 7 days"this year"- January 1 to December 31 of the current year
Why this is useful: Your filter automatically updates. No need to change code every month!
Example 6: Match Any Value (OR Logic Within One Field)Find orders with status "Pending" OR "In Progress":
{
"fields": ["Id", "OrderNumber", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "Includes",
"values": ["Pending", "In Progress"]
}
]
}
Find all customers EXCEPT archived ones:
The include parameter:
"include": true(default) = Include matching records"include": false= Exclude matching records (basically adds a NOT)
{
"fields": ["Id", "Name", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Archived"],
"include": false
}
]
}
Building Block 2: Complex Logic with "whereGroups"
When to use whereGroups: You need OR logic between different fields.
Important concept:where= AND only (simpler, but limited)whereGroups= AND + OR (complex, but powerful)
Example 8: Complex OR logic with groups
Find customers in California OR Texas who are active:
{
"fields": ["Id", "Name", "State"],
"whereGroups": [
{
"operator": "OR",
"subGroups": [
{
"operator": "OR",
"conditions": [
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["California"]
},
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["Texas"]
}
]
}
]
}
]
}
Example 9: Combining AND + OR Logic
Find active customers in California OR Texas:
{
"fields": ["Id", "Name", "State", "Status"],
"whereGroups": [
{
"operator": "AND",
"subGroups": [
{
"operator": "AND",
"conditions": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
}
]
},
{
"operator": "OR",
"conditions": [
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["California"]
},
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["Texas"]
}
]
}
]
}
]
- from customers, First, filter by Status = Active AND in California OR Texas .
- Result: Active customers in California OR Texas only
Find high-value customers OR recent signups:
This is the formula: (Amount > 10000 AND Type = Enterprise) OR (CreatedOn in last 30 days AND Status = Trial)
{
"fields": ["Id", "Name", "Amount", "Type", "Status"],
"whereGroups": [
{
"operator": "OR",
"subGroups": [
{
"operator": "AND",
"conditions": [
{
"fieldName": "Amount",
"operator": "GreaterThan",
"values": [10000]
},
{
"fieldName": "Type",
"operator": "EqualTo",
"values": ["Enterprise"]
}
]
},
{
"operator": "AND",
"conditions": [
{
"fieldName": "CreatedOn",
"operator": "RelativeMatch",
"values": ["last 30 day"]
},
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Trial"]
}
]
}
]
}
]
}
- The top-level operator is OR (either group qualifies)
- Group 1: High-value customers (both conditions must be true)
- Group 2: Recent trials (both conditions must be true)
- Result: Records matching Group 1 OR Group 2
How to Read Complex whereGroups
Follow this pattern:
- Start at the top - What's the operator? (AND or OR)
- Look at subGroups - How many groups are there?
- Inside each subGroup - What's the operator? (AND or OR)
- Check conditions - What are the actual filters?
Quick Tips for whereGroups
-
Start simple: Build with where first, add whereGroups only when you need OR logic
-
Test incrementally:
- Test each condition individually
- Combine with AND logic
- Add OR logic last
-
Common mistake: Forgetting that where and whereGroups are BOTH applied
Final filter = where conditions AND whereGroups conditions
💡 Need more filter options? Check out the complete Filters Reference Guide for all available operators by field type.
Get Record by ID
Retrieve a single record by its ID.
https://api.apper.io/v1/data/{appId}/tables/{tableName}/{recordId}
Response:
{
"success": true,
"message": "",
"data": {
"Id": 2,
"Name": "John Doe",
"email": "[email protected]",
"email_2": "[email protected]",
"company": null
}
}
Create Record
Create one or more records in a table.
https://api.apper.io/v1/data/{appId}/tables/{tableName}/records
Request Body:
{
"records": [
{
"Name": "Alice Johnson",
"Email": "[email protected]",
"Status": "Active",
},
{
"Name": "Daniel Nielsen",
"Email": "[email protected]",
"Status": "Active",
“ID_c” : 225
}
]
}
Field Value Formats:
Different field types need different formats:
Note: File uploads are not currently supported via API.
| Field Type | Format | Example |
|---|---|---|
| Text, Email, | String | "[email protected]" |
| Boolean | true/falsetrue | true |
| Date | ISO 8601 date | "2025-01-15" |
| DateTime | ISO 8601 datetime | "2025-01-15T14:30:00" |
| Picklist, Multipicklist | Comma-separated | "marketing, lead, priority" |
| Lookup, MasterDetail | Parent record ID | 2 |
| People | Array of user objects | [{"User": 2006}] |
Response:
{
"success": true,
"message": "Bulk create operation completed",
"results": [
{
"success": true,
"message": "",
"data": {
"Id": 26,
"Name": "Alice Johnson",
"Email": "[email protected]",
"Status": "Active",
"Amount": 1500,
"CreatedOn": "2026-01-20T06:11:23",
"CreatedBy": {
"Id": 1,
"Name": "John Doe"
},
"ModifiedOn": "2026-01-20T06:11:23",
"ModifiedBy": {
"Id": 1,
"Name": "John Doe"
}
}
},
{
"success": false,
"message": "",
"errors": [
{
"fieldName": "ID_c",
"fieldLabel": "ID_c",
"message": "Unknown field `ID_c` provided"
}],
"data": null
}
]
}
Understanding Bulk Responses:
- Top-level
success: truemeans the bulk operation completed (not that all records succeeded) - Check the individual
resultsarray for per-record success/failure results[0]corresponds torecords[0], and so on- Each result has its own
success, message, anddataorerrors
Update Record
Update existing records.
https://api.apper.io/v1/data/{appId}/tables/{tableName}/records
Request Body
{
"records": [
{
"Id": 2,
"Status": "Active",
"Notes": "Updated via API"
},
{
"Id": 4,
"Status": "Inactive"
}
]
}
- You must include the Id field for each record
- Only include fields you want to update (other fields remain unchanged)
Response:
{
"success": true,
"message": "Bulk update operation completed",
"results": [
{
"success": true,
"message": "Record updated successfully!",
"data": {
"Id": 2,
"Name": "Alice Johnson",
"Email": "[email protected]",
"Status": "Active",
"Notes": "Updated via API",
"Amount": 1500,
"ModifiedOn": "2026-01-20T10:25:15",
"ModifiedBy": {
"Id": 1,
"Name": "John Doe"
}
}
},
{
"success": true,
"message": "Record updated successfully!",
"data": {
"Id": 4,
"Name": "Bob Smith",
"Email": "[email protected]",
"Status": "Inactive",
"Amount": 2300,
"ModifiedOn": "2026-01-20T10:25:15",
"ModifiedBy": {
"Id": 1,
"Name": "John Doe"
}
}
}
]
}
Understanding Bulk Results:
- Check the
resultsarray for individual outcomes results[0]corresponds to the first record in your request- Failed updates show specific error messages
Delete Record
Remove records permanently.
https://api.apper.io/v1/data/{appId}/tables/{tableName}/records
Request Body:
{
"recordIds": [1, 2, 5, 8]
}
Response:
{
"success": true,
"message": "Bulk delete operation completed",
"results": [
{
"success": true,
"message": "Record deleted successfully",
"data": {},
},
{
"success": true,
"message": "Record deleted successfully",
"data": {},
},
{
"success": true,
"message": "Record deleted successfully",
"data": {},
},
{
"success": true,
"message": "Record deleted successfully",
"data": {},
}
]
}