> ## Documentation Index > Fetch the complete documentation index at: https://support.tulip.co/llms.txt > Use this file to discover all available pages before exploring further. # Run an aggregate function (e.g. average, mode) on a table's field. > Requires `tables:read` API scope. Attempting to run a numeric aggregate function e.g. sum on a non-numeric field type will generate a 422 error response. The only non-numeric functions are `mode` and `uniqueValues`. ## OpenAPI ````json GET /tables/{tableId}/runAggregation { "openapi": "3.0.3", "info": { "title": "Tulip API", "description": "The Tulip API gives you programmatic access to your Tulip data.\n\n### Query Parameters\n\nSome Tulip API endpoints take parameters specified in the [URL query](https://en.wikipedia.org/wiki/Query_string). In the Tulip API, the URL query is expected to be a list of key-value pairs, with pairs separated by `&`, keys and values separated by `=`, and keys and values being [percent-encoded](https://en.wikipedia.org/wiki/Percent-encoding). Keys are parsed as strings, values *must* be present for each key, and values are parsed as JSON. Simple string values may be specified as-is instead of being JSON-encoded. A query parameter may be specified multiple times, but only the final value will be used. Examples (shown without percent encoding for readability):\n\n * `a=1&b=true&c=\"hello\"` would parse a parameter `a` as the number 1, `b` as the boolean `true`, and `c` as the string `hello`\n * `a=foo&b=\"foo\"` would parse a parameter `a` as the string `foo` and `b` as the string `foo`\n * `data={\"points\":[{\"x\":3,\"y\":4},{\"x\":2,\"y\":-9}]}` would parse a parameter `data` as an object with a single field `points` containing an array with two elements: an object with `x` as the number 3 and `y` as the number 4, and an object with `x` as the number 2 and `y` as the number -9\n * `a=foo&a=bar` would parse a parameter `a` as the string `bar`\n", "termsOfService": "https://tulip.co/tos/", "contact": { "name": "Tulip Interfaces", "url": "https://tulip.co/contact-us/", "email": "sales@tulip.co" }, "version": "3.0" }, "servers": [ { "url": "http://localhost:8088/api/v3", "description": "Cypress Tulip API Server" } ], "paths": { "/tables/{tableId}/runAggregation": { "get": { "summary": "Run an aggregate function (e.g. average, mode) on a table's field.", "description": "Requires `tables:read` API scope.\n\nAttempting to run a numeric aggregate function e.g. sum on a non-numeric field type will generate a 422 error response.\nThe only non-numeric functions are `mode` and `uniqueValues`.\n", "x-tulip-visibility": "public", "tags": [ "Tulip Tables" ], "security": [ { "apiKey": [ "tables:read" ] } ], "parameters": [ { "$ref": "#/components/parameters/tableId" }, { "name": "fieldId", "in": "query", "required": true, "description": "The field to aggregate on.\nAn invalid field name for the specified table will result in a 404 error.\n", "schema": { "type": "string" } }, { "name": "function", "in": "query", "required": true, "description": "Aggregate function names are case-insensitive.\nAn invalid function type will result in a 400 error.\n", "schema": { "type": "string", "enum": [ "sum", "count", "avg", "min", "max", "mode", "uniqueValues" ] } }, { "name": "limit", "in": "query", "required": true, "description": "The maximum number of table records to evaluate.", "schema": { "type": "integer", "minimum": 1, "maximum": 100000, "example": 10 } }, { "$ref": "#/components/parameters/sortOptions" }, { "$ref": "#/components/parameters/tableFilters" }, { "$ref": "#/components/parameters/tableFilterAggregator" }, { "$ref": "#/components/parameters/decimalHeaderWithSortingNote" } ], "responses": { "200": { "description": "The count of Tulip Table records was retrieved successfully.\n", "content": { "application/json": { "schema": { "type": "object", "properties": { "result": { "type": "number" } } } } } }, "400": { "$ref": "#/components/responses/badRequestError" }, "401": { "$ref": "#/components/responses/authenticationRequiredError" }, "403": { "$ref": "#/components/responses/authenticationFailedError" }, "404": { "$ref": "#/components/responses/notFoundError" }, "422": { "$ref": "#/components/responses/unprocessableEntityError" }, "500": { "$ref": "#/components/responses/internalServerError" } } } } }, "components": { "parameters": { "tableId": { "name": "tableId", "description": "The id of a Tulip Table.\n", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/ObjectId" } }, "sortOptions": { "name": "sortOptions", "in": "query", "description": "Sorting allows to define which records are considered if there are more than the specified limit.\nSort priority is determined by the order or options, i.e. sort by the first option, them by the second, etc.\nIf omitted, there is no guarantee as to which records are selected.\nA sortBy value that is not one of the table's field will generate a 422 error.\nOptions for sortDir are \"asc\" and \"desc\".\n\nExample: `[{\"sortBy\": \"superAwesomeField\", \"sortDir\": \"asc\"}, {\"sortBy\": \"lessAwesomeField\", \"sortDir\": \"desc\"}]`\n", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TulipTableSortOption" } } } } }, "tableFilters": { "name": "filters", "in": "query", "description": "An optional array of filter expressions to filter the Table records by. Each filter is an object specifying the `field` (name of a table column), `functionType` (comparison function), and `arg` (the value to compare to). For example:\n```json\n[\n {\"field\": \"field1\", \"functionType\": \"equal\", \"arg\": \"hello\"},\n {\"field\": \"field2\", \"functionType\": \"notBlank\"},\n {\"field\": \"field3\", \"functionType\": \"isIn\", \"arg\": [\"a\", \"b\", \"c\"]}\n]\n```\n\nThe field may be the name of a table column (including `id`) or one of these special values:\n * `_createdAt` (datetime)\n * `_updatedAt` (datetime)\n\nLinked record columns (table links) cannot be used as filter fields.\n\nThe valid function types are:\n\n| Functions | `arg` |\n|-------------------------------------------------------------------------------------|-----------------------------------------------|\n| `equal`, `notEqual` | Single value |\n| `blank`, `notBlank` | None |\n| `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual` | Single value |\n| `isIn`, `notIsIn` | JSON array
`field` may not be a datetime |\n| `contains`, `notContains`, `startsWith`, `notStartsWith`, `endsWith`, `notEndsWith` | Single value
`field` must be a text column |\n\n#### Format by column type\n\nThe required format of `arg` depends on the `field` column's type:\n\n| Column type | `arg` format | Example |\n|------------------------------------|---------------------------------------------------------------------------------|------------------------------------------|\n| **text**, **`id`** | JSON string | `\"hello\"` |\n| **integer** | JSON number1 | `123` |\n| **number** | JSON number or decimal string `\"[-]xx[.yyy]\"` | `456.7` or `\"456.7\"` |\n| **boolean** | JSON boolean or equivalent string | `true` or `\"true\"` |\n| **datetime** | ISO 8601 string or Unix timestamp (integer seconds since epoch) | `\"2024-01-15T10:30:00Z\"` or `1736936400` |\n| **duration** | JSON number (seconds) | `300` (for 5 minutes) |\n| **color** | JSON object with integer fields `r`, `g`, `b` (0–255) and float field `a` (0–1) | `{\"r\": 255, \"g\": 128, \"b\": 0, \"a\": 1.0}` |\n| **imageUrl**, **fileUrl** | JSON string (the URL) | `\"https://example.com/photo.jpg\"` |\n| **user**, **machine**, **station** | JSON string (resource ID) | `\"abc123xyz\"` |\n\n1: For `isIn` and `notIsIn` _only_, the list may contain either JSON numbers or JSON strings: `[1, \"2\"]`. This is supported for historical reasons and backwards compatibility.\n", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "object", "properties": { "field": { "type": "string" }, "functionType": { "type": "string", "enum": [ "equal", "notEqual", "blank", "notBlank", "greaterThanOrEqual", "lessThanOrEqual", "greaterThan", "lessThan", "contains", "notContains", "startsWith", "notStartsWith", "endsWith", "notEndsWith", "isIn", "notIsIn" ] }, "arg": {} }, "required": [ "field", "functionType" ] } } } } }, "tableFilterAggregator": { "name": "filterAggregator", "in": "query", "description": "How the filters in the `filters` parameter are combined.\n- `all` means that every filter must match a record in order for the record to be included.\n- `any` means at least one filter must match a record in order for the record to be included.\n", "schema": { "$ref": "#/components/schemas/TulipTableFilterAggregator" } }, "decimalHeaderWithSortingNote": { "name": "X-Tulip-Number-Format", "in": "header", "required": false, "description": "The format that Tulip should use in the response when formatting Tulip numbers. If omitted or set to\n`float`, Tulip will format numbers as JSON numbers. If set to `decimal`, Tulip will format numbers as strings\nrepresenting the decimal representation of the number, like `\"1.20\"`. Tulip will only output trailing zeros\nwhen using `decimal` formatting.\n\n#### Filtering\n\nTulip interprets JSON numbers as floating-point values, which may result in edge cases when this header's value\nis `decimal`. If you set this header to `decimal`, we recommend passing values for Tulip number fields as strings.\n\nSpecifically, if this header is set to `decimal` and `filters` specifies a Tulip number field, that filter's `arg`\nwill be interpreted as a decimal value. If it's passed in as a string, it must be of format `\"[-]xx[.yyy]\"`. If it's\npassed in as a JSON number, it will be parsed as a JavaScript number, and then converted to a decimal string. This\nmay result in loss of precision, since JavaScript numbers have limited precision (roughly 15-17 digits).\n\nFor example, the number `2.0000000000000002` cannot be expressed as a JavaScript number, so it will be parsed as\n`2`. This means that if you are trying to look up a value with\n`{... \"functionType\", \"equal\", \"arg\", 2.0000000000000002}`, Tulip will not find that value (since it will instead\nlook for values that equal `2`). To specify filters for such numbers when this header is `decimal`, we recommend you\npass them in as strings, like `\"2.0000000000000002\"`.\n\n#### Sorting\n\nIf this header is set to `decimal` and `sortOptions` specifies a Tulip number field, that field will be ordered\nby value (as normal) and then by number of trailing zeros, with fewer zeros sorting earlier. For example, `1.2`\nwill sort before `1.20`. If the header is unset or set to `float`, trailing zeros will not be taken into\nconsideration when sorting. This discrepancy in the handling of trailing zeros can result in inconsistent ordering\nif `sortOptions` specifies multiple fields. For example, with records:\n\n| Id | myNumber |\n|----|----------|\n| A | 1.20 |\n| B | 1.2 |\n\nSorting by `myNumber, Id` with this header set to `decimal` will result in:\n\n```json\n[\n {\"myNumber\": \"1.2\", \"Id\": \"B\"},\n {\"myNumber\": \"1.20\", \"Id\": \"A\"}\n]\n```\n\n... because `\"1.20\"` sorts before `\"1.2\"`. On the other hand, without this header, the result will be:\n\n```json\n[\n {\"myNumber\": 1.2, \"Id\": \"A\"},\n {\"myNumber\": 1.2, \"Id\": \"B\"}\n]\n```\n\n... because the numbers are tied, so the secondary sorting on `Id` factors in.\n\nNote that this header only affects Tulip numbers. Other types, including Tulip integers, are unaffected.\n", "schema": { "type": "string", "enum": [ "decimal", "float" ], "default": "float" } } }, "responses": { "badRequestError": { "description": "The request was malformed. This could mean that headers, query parameters, or the request body was unable to be parsed or had unexpected values.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } }, "authenticationRequiredError": { "description": "The request was made unauthorized. HTTP Basic Authorization using a Tulip API Key is required for use of the API.\n", "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/ApiError" }, { "type": "object", "properties": { "errorCode": { "enum": [ "AuthenticationRequired" ] } } } ] } } } }, "authenticationFailedError": { "description": "The provided authentication info was rejected. The response will provide additional details.\n", "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/ApiError" }, { "type": "object", "properties": { "errorCode": { "enum": [ "AuthenticationFailed" ] } } } ] } } } }, "notFoundError": { "description": "The requested database entry was not found.\n", "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/ApiError" }, { "type": "object", "properties": { "errorCode": { "enum": [ "NoSuchDatabaseEntry" ] } } } ] } } } }, "unprocessableEntityError": { "description": "The request was syntactically sound, but could not be processed due to a logical problem.\n", "content": { "application/json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/ApiError" }, { "type": "object", "properties": { "errorCode": { "enum": [ "UnprocessableEntity" ] } } } ] } } } }, "internalServerError": { "description": "The server encountered an unexpected error.\n", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } } } }, "securitySchemes": { "apiKey": { "type": "http", "scheme": "basic", "description": "Access to the Tulip API requires the use of [HTTP Basic Authentication](https://datatracker.ietf.org/doc/html/rfc7617) using the credentials of an active [Tulip API Token](https://support.tulip.co/docs/set-up-a-tulip-api). All requests require the `Authorization` HTTP header with the `Basic` scheme to provide API credentials unless otherwise noted.\n\nAPI tokens can be configured with a set of _scopes_ which determine what parts of the API that specific token has access to. Security best practices dictate that API credentials be given the minimum set of capabilities required to fulfill their intended purpose. For example, an API token created for use in an integration that is only designed to use the Tables API should probably only be given the `tables:read` and `tables:write` scopes. In this way you can minimize risk in the event that API credentials are compromised. API endpoints will document what API token scopes are required to access that endpoint in their descriptions. If a request is made to an endpoint with an API token which does not have the required scopes, the response will be an authorization error.\n" } }, "schemas": { "ObjectId": { "$ref": "#/components/schemas/RandomId" }, "TulipTableSortOption": { "x-tulip-visibility": "public", "description": "Sorts are used to order the queries based on columns defined in the Tulip Table where the query exists.\n", "example": { "sortBy": "Color", "sortDir": "asc" }, "type": "object", "properties": { "sortBy": { "description": "The field that records should be sorted by in the response. Can be the name of any of the columns of the Table (including `id`) or one of these special values:\n* `_sequenceNumber`\n* `_createdAt`\n* `_updatedAt`\n", "type": "string" }, "sortDir": { "description": "The direction of the records, either ascending or descending\n", "type": "string", "enum": [ "asc", "desc" ], "default": "asc" } }, "required": [ "sortBy", "sortDir" ] }, "TulipTableFilterAggregator": { "description": "How the filters in a table filter are combined.\n- `all` means that every filter must match a record in order for the record to be included.\n- `any` means at least one filter must match a record in order for the record to be included.\n", "type": "string", "enum": [ "all", "any" ], "default": "all" }, "ApiError": { "x-tulip-visibility": "public", "type": "object", "properties": { "errorCode": { "type": "string" }, "errorUniqueID": { "$ref": "#/components/schemas/RandomSecret" }, "details": { "type": "string" } }, "required": [ "errorCode", "details" ] }, "RandomId": { "x-tulip-visibility": "public", "example": "g56RCoZCtzv7borvp", "type": "string", "pattern": "^[a-zA-Z0-9_]+$" }, "RandomSecret": { "x-tulip-visibility": "public", "example": "aq21mSKC1rbO87TjC/4Hz2EJHd/v+jxf7MtC315vo0Y", "type": "string", "pattern": "^[a-zA-Z0-9+/]+$" } } } } ````