Skip to main content

Endpoints

Apps

List All Apps

This endpoint returns all apps accessible to the authenticated user.

GET
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:

FieldTypeDescription
appIdstringApp identifier (use as appId in other endpoints)
labelstringDisplay name
createdOndatetimeISO 8601 timestamp
modifiedOndatetimeISO 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

GET
https://api.apper.io/v1/meta/{appId}/tables

Path Parameters:

ParameterTypeDescription
appIdstringApp 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.

GET
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:

TypeDescriptionValue Format
NumberNumeric valuesInteger or decimal
TextPlain textString
EmailEmail addressValid email string
DateDates only"2025-01-15"
DateTimeDates with time"2025-01-15T14:30:00"
BooleanTrue/False checkboxestrue or false
PicklistSingle-select dropdownString from options array
MultipicklistMulti-select dropdownComma-separated options
LookupReference to another tableRecord ID (integer)
MasterDetailParent-child relationshipRecord 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.

POST
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:

FieldTypeDescription
fieldsarrayWhich fields to include in response (omit for all fields)
orderByarraySort configuration
orderBy[].fieldNamestringField to sort by
orderBy[].sortTypestringAsc or Desc
pagingInfo.limitintegerRecords per page (default: 20, max: 100)
pagingInfo.offsetintegerNumber 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.

POST
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:

FieldTypeDescription
fieldsarrayField names to return
wherearraySimple filter conditions (AND combined)
whereGroupsstringComplex grouped filter conditions (AND/OR logic)
orderBystringAsc or Desc
pagingInfointegerMax records to return (default: 20)
groupByarrayFields to group results by
aggregatorsarraySub-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:

ParameterWhat It IsExample
fieldNameWhich field to filter"Status", "Email", "Amount"
operatorHow to compare"EqualTo", "Contains", "GreaterThan"
valuesWhat to compare against["Active"] or ["1000"]
includeInclude (true) or exclude (false) matchestrue (default)

Common Operators Quick Reference

Supported Operators:

OperatorWhat It DoesExample Use Case
EqualToExact matchStatus = "Active"
NotEqualToNot equalStatus ≠ "Archived"
ContainsContains textName contains "Tech"
StartsWithBegins withPhone starts with "+1"
EndsWithEnds withEmail ends with ".com"
GreaterThanNumeric >Amount > 1000
LessThanNumeric <Stock < 10
GreaterThanOrEqualToNumeric >=Score >= 75
LessThanOrEqualToNumeric <=Age <= 65
IncludesMatch any of (OR)Status in ["Active", "Pending"]
ExcludesMatch none ofType not in ["Draft", "Deleted"]
info

💡 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 condition

Find all active customers:

{
"fields": ["Id", "Name", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
}
]
}
Example 2: Multiple conditions (AND logic)

Find active customers in California:

{
"fields": ["Id", "Name", "State", "Status"],
"where": [
{
"fieldName": "Status",
"operator": "EqualTo",
"values": ["Active"]
},
{
"fieldName": "State",
"operator": "EqualTo",
"values": ["California"]
}
]
}
Example 3: Text Search

Find companies with "Tech" in their name:

{
"fields": ["Id", "Name"],
"where": [
{
"fieldName": "Name",
"operator": "Contains",
"values": ["Tech"]
}
]
}
Example 4: Numeric Range

Find orders over $1000:

{
"fields": ["Id", "OrderNumber", "Amount"],
"where": [
{
"fieldName": "Amount",
"operator": "GreaterThan",
"values": [1000]
}
]
}
Example 5: Date Filtering (Dynamic Dates)

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"]
}
]
}
Example 7: Excluding Results

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"]
}
]
}
]
}
]
Step-by-step breakdown:
  • from customers, First, filter by Status = Active AND in California OR Texas .
  • Result: Active customers in California OR Texas only
Example 10: Complex Multi-Condition Logic

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"]
}
]
}
]
}
]
}

Breaking it down:

  • 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
info

💡 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.

POST
 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.

POST
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 TypeFormatExample
Text, Email,String"[email protected]"
Booleantrue/falsetruetrue
DateISO 8601 date"2025-01-15"
DateTimeISO 8601 datetime"2025-01-15T14:30:00"
Picklist, MultipicklistComma-separated"marketing, lead, priority"
Lookup, MasterDetailParent record ID2
PeopleArray 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: true means the bulk operation completed (not that all records succeeded)
  • Check the individual results array for per-record success/failure
  • results[0] corresponds to records[0], and so on
  • Each result has its own success, message, and data or errors

Update Record

Update existing records.

PUT
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 results array for individual outcomes
  • results[0] corresponds to the first record in your request
  • Failed updates show specific error messages

Delete Record

Remove records permanently.

DELETE
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": {},
}
]
}