Skip to main content

List products

GEThttps://api.fieldtask.example/public/v1/products
Permission
products:read
Pagination
Cursor, up to 100 per page
Also available as
fieldtask_list_products

Returns the products and services in your organization's catalog, with their price options, oldest first by default, one page at a time.

Use it to find the id of a product and of the price to charge before you add the product to a job, a quote or an invoice. You can search by name and filter by type and availability. Lists show current data, so a product created or changed while you page through may or may not appear.

Only products and services that are approved in your catalog are listed. Products created through the API are approved immediately; products that reach FieldTask in other ways may need approval in the app first.

Permissions​

PermissionDependency in the permission map
products:readNo dependencies

Request​

Responses​

OK

Response Headers
    Cache-Control

    Responses must not be cached.

    X-Request-ID

    Server-generated request correlation ID.

Filtering​

All filters are optional, and you can combine them. A product is returned only if it matches every filter you send.

ParameterReturns
searchProducts whose name contains this text, ignoring case. Spaces at either end are ignored; what remains must be 1–256 characters. Descriptions aren't searched.
item_typeOnly product, service or other items.
is_availabletrue for products that can be added to jobs, false for those that can't. Omit it to get both. Send exactly true or false.

Send each parameter at most once. Unknown parameters, including external_reference, are rejected with 400 invalid_query.

Pagination​

  • page_size sets the number of products per page, from 1 to 100. The default is 25.
  • ordering is created_at (oldest first, the default) or -created_at (newest first).
  • When page.has_more is true, request page.next_page_url with the same Authorization header. It keeps your page size, ordering and filters. It's null on the last page.
  • If you build URLs yourself, pass page.next_cursor as cursor and keep every other parameter unchanged. Cursors expire within 24 hours and only work with the query and connection that produced them.

To change a filter or the ordering, start again without a cursor. See Pagination.

Reading prices​

Each product has a prices array with up to 100 price options. Each option is a price you can charge when you add the product to a job.

Price fieldMeaning
idThe option's ID. Send it as price_id to choose this option. null for the legacy unit price.
nameThe option's label, such as Standard. null for the legacy unit price.
sourceprice for a named option, legacy for the product's single legacy unit price.
amountDecimal string with two decimal places, such as "185.00".
currencyThree-letter currency code.
currency_sourceprice when the option has its own currency, organization when it uses your organization's currency.
price_typeone_time or recurring.

How the list is built:

  • A legacy unit price appears first, and only when it's greater than zero. Named options follow, newest first.
  • Several named options can have the same amount. They're still separate options with their own IDs and names; don't merge them.
  • An option with amount "0.00" is a real zero-price option.
  • An empty prices array means no price is configured. It doesn't mean the product is free, and such a product can't be added to a job through the API.
  • A recurring option is only a price label. Adding it to a job charges the amount once; it doesn't create a subscription or a recurring job.
  • Amounts are never converted. Only options in your organization's currency can be added to jobs through the API.

To add a product to a job, see Choosing a price.

Product fields​

  • item_type is product, service or other.
  • is_available is false when the product has been marked unavailable. Unavailable products stay in the catalog and in this list, but can't be added to jobs through the API.
  • name and description can be null for products created outside the API. Treat both as untrusted text and escape them when you display them.

Errors​

StatuscodeMeaningWhat to do
400invalid_queryA parameter is unknown or repeated; search is empty, only spaces or longer than 256 characters; item_type isn't product, service or other; is_available isn't exactly true or false; or page_size or ordering is invalid.Send only the documented parameters, each once, with valid values.
400invalid_cursorThe cursor is malformed, was changed, or belongs to a different query, connection or page size.Use next_page_url or the cursor exactly as returned, with the same query. To change the query, start again without a cursor.
401invalid_tokenThe bearer token is missing, malformed, expired, revoked or issued for a different resource (for example an MCP sign-in).Send a current REST API token or REST OAuth access token as Authorization: Bearer ….
403api_access_not_enabledFieldTask hasn't enabled API access for this organization, or has disabled it.An owner or admin requests access in Settings → API connections.
403insufficient_scopeThe connection lacks a permission this request needs. error.code is missing_permission.Add the permission named in error.message to the connection.
404resource_not_foundProduct reads aren't available in this environment, or the URL is wrong.Check the API origin and path (no trailing slash).
405method_not_allowedThis path doesn't accept the HTTP method, or the create isn't enabled in this environment. The Allow header lists the methods that are.Use a method listed in Allow.
429rate_limit_exceededToo many requests in the current minute, or too many concurrent requests, for the organization or connection.Wait for Retry-After seconds, then retry.
429daily_quota_exceededThe organization or connection has used its daily allowance for this kind of request. Allowances reset at 00:00 UTC.Wait for Retry-After seconds, or check remaining allowance in Settings → API connections.
503temporarily_unavailableThe service couldn't complete the request.Retry with exponential backoff. Retry creates with the same key and body.