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.

POST
https://api.apper.io/v1/data/{appId}/tables/{tableName}/records/delete
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": {},
}
]
}