> For the complete documentation index, see [llms.txt](https://docs.sprinthub.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sprinthub.com/en/topics/api/api-list-funnel-opportunities-with-filters-and-pagination.md).

# API - List Funnel Opportunities with Filters and Pagination

Complements the page [API - API Call to List Opportunities for a Lead](/en/topics/api/api-api-call-to-list-a-leads-opportunities.md). While that endpoint returns all opportunities of **a specific lead**, this returns the opportunities of **an entire funnel**, with filters, implicit Kanban sorting, pagination and, above all, the ability to paginate **column by column (stage by stage)** through the parameter `columnId`.

It is the same endpoint that the CRM interface uses to build the Kanban: each column loads its own page of cards, independently of the others.

### List opportunities in a funnel

**`POST`** `/crm/opportunities/{funnelId}?i=suainstancia&apitoken=xxxxxx`

`{funnelId}` = funnel ID (CRM). In the example below, `8`.

Full endpoint: `https://sprinthub-api-master.sprinthub.app/crm/opportunities/8?i=suainstancia&apitoken=xxxxxx`

> **API base.** `https://sprinthub-api-master.sprinthub.app` is the base URL. Replace `suainstancia` with your instance name, the same one that appears in the dashboard URL, and `xxxxxx` with your API key.

#### URL parameters

| Parameter  | Required | Description                                |
| ---------- | -------- | ------------------------------------------ |
| `i`        | Yes      | Instance name. Example: `?i=suainstancia`. |
| `apitoken` | Yes      | Instance API key.                          |

#### Headers

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |

The returned opportunities respect the permissions linked to the API key (Departments, Permission Groups, and funnel permissions).

***

### Before you start: finding the `funnelId` and the `columnId`

The `funnelId` of the URL is the CRM ID (funnel). To list all funnels in the instance in a single GET call:

```bash
curl --location 'https://sprinthub-api-master.sprinthub.app/crm?i=suainstancia&apitoken=xxxxxx'
```

Each returned funnel brings its ID, which is the value used in `/crm/opportunities/{funnelId}`, and the funnel stages, whose IDs populate the parameter `columnId` of the request body.

The stage IDs also appear in the field `crm_column` of each opportunity returned by this endpoint, which serves as a quick check.

### Request body

```json
{
    "filterByUsers": [],
    "filterByStatus": ["open"],
    "filterByCreateDate": null,
    "filterByExpectedCloseDate": null,
    "filters": [],
    "search": "",
    "searchBy": "lead",
    "page": 5,
    "limit": 25,
    "columnId": 58,
    "onlyArchived": false
}
```

#### Parameter reference

| Parameter                   | Type              | Default  | Description                                                                                                       |
| --------------------------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `filterByUsers`             | array of integers | `[]`     | IDs of the users responsible for the opportunities. Empty returns all responsible users visible to the session.   |
| `filterByStatus`            | array of strings  | `[]`     | Opportunity statuses: `open`, `gain`, `lost`. Accepts more than one. Empty returns all.                           |
| `filterByCreateDate`        | object or `null`  | `null`   | Opportunity creation period. `null` turns off the filter.                                                         |
| `filterByExpectedCloseDate` | object or `null`  | `null`   | Expected close period. `null` turns off the filter.                                                               |
| `filters`                   | array of objects  | `[]`     | Advanced filters (combined conditions over opportunity, lead, and company fields). Empty turns off the filter.    |
| `search`                    | string            | `""`     | Text search term. Empty turns off search.                                                                         |
| `searchBy`                  | string            | `"lead"` | Defines where the term of `search` is applied. `lead` searches the data of the contact linked to the opportunity. |
| `page`                      | integer           | `1`      | Desired page. The first page is `1`.                                                                              |
| `limit`                     | integer           | `25`     | Number of opportunities per page.                                                                                 |
| `columnId`                  | integer           | -        | Column ID (stage) of the funnel. Restricts the result to a single stage and paginates only within it.             |
| `onlyArchived`              | boolean           | `false`  | `true` returns only archived opportunities. `false` returns only non-archived ones.                               |

#### Filter details

**`filterByUsers`**

Filters by the opportunity owner. Corresponds to the **Owner** filter in the interface.

```json
"filterByUsers": [12, 47]
```

Returns opportunities whose owner is user 12 or 47. The empty array does not mean "no owner": it means "no owner filter".

**`filterByStatus`**

Corresponds to the **Status**.

| Value  | Meaning          |
| ------ | ---------------- |
| `open` | Open opportunity |
| `gain` | Won opportunity  |
| `lost` | Lost opportunity |

```json
"filterByStatus": ["gain", "lost"]
```

> **Attention.** The view of won and lost opportunities depends on the permission **CRM - Won and Lost Opportunities** of the user's group. If the API key does not have this permission, the filter will not return these records.

**`filterByCreateDate` and `filterByExpectedCloseDate`**

They filter by period: opportunity creation date and expected close date, respectively. Correspond to the filters **Creation date** and **Expected close date** filter in the interface.

```json
"filterByCreateDate": {
    "start": "2026-08-01",
    "end": "2026-08-31"
}
```

Send `null` (or omit) turns off the filter. The two filters are independent and can be combined in the same request.

**`filters`**

Receives the **Advanced Filters** of the CRM: conditions built over opportunity, lead, or company fields, including custom fields. Each item in the array represents a condition, and the conditions are combined with each other.

```json
"filters": [
    {
        "field": "custom_field_id_ou_nome",
        "operator": "equals",
        "value": "Premium Plan"
    }
]
```

The safest way to discover the exact structure of a condition is to build it in the CRM interface and inspect the request body sent by the browser. The empty array is equivalent to "no advanced filters".

**`search` and `searchBy`**

`search` is the typed term. `searchBy` defines the search target. With `searchBy: "lead"`, the term is compared with the data of the contact linked to the opportunity (name, phone, email), and not with the card title.

```json
"search": "maria",
"searchBy": "lead"
```

`search` empty ignores `searchBy`.

**`onlyArchived`**

Switches between the two base views. There is no combined view: the request either returns archived or non-archived.

```json
"onlyArchived": true
```

***

### Pagination by funnel column

This is the central point of this endpoint. The CRM does not paginate the funnel as a single list: each Kanban column is its own sequence of pages.

* With `columnId` filled, `page` and `limit` navigate **only within that stage**. `page: 5` with `limit: 25` returns the opportunities from the 101st to the 125th position **of that column**, in the order they appear in the Kanban.
* Without `columnId`, `page` and `limit` they navigate the entire funnel, mixing the stages.
* The filters are applied **before** pagination. Changing any filter resets the count: go back to `page: 1`.
* The end of the column is detected when the response returns fewer items than the `limit` requested, or when it returns an empty array.

#### Example: fifth page of column 58

```bash
curl --location 'https://sprinthub-api-master.sprinthub.app/crm/opportunities/8?i=suainstancia&apitoken=xxxxxx' \\
--header 'Content-Type: application/json' \\
--data '{
    "filterByUsers": [],
    "filterByStatus": ["open"],
    "filterByCreateDate": null,
    "filterByExpectedCloseDate": null,
    "filters": [],
    "search": "",
    "searchBy": "lead",
    "page": 5,
    "limit": 25,
    "columnId": 58,
    "onlyArchived": false
}'
```

#### Example: entire funnel, without separating by stage

Just omit `columnId`.

```bash
curl --location 'https://sprinthub-api-master.sprinthub.app/crm/opportunities/8?i=suainstancia&apitoken=xxxxxx' \\
--header 'Content-Type: application/json' \\
--data '{
    "filterByUsers": [],
    "filterByStatus": ["open"],
    "filterByCreateDate": null,
    "filterByExpectedCloseDate": null,
    "filters": [],
    "search": "",
    "searchBy": "lead",
    "page": 1,
    "limit": 50,
    "onlyArchived": false
}'
```

#### Example: won this month, from two sellers, in one stage

```bash
curl --location 'https://sprinthub-api-master.sprinthub.app/crm/opportunities/8?i=suainstancia&apitoken=xxxxxx' \\
--header 'Content-Type: application/json' \\
--data '{
    "filterByUsers": [12, 47],
    "filterByStatus": ["gain"],
    "filterByCreateDate": {
        "start": "2026-08-01",
        "end": "2026-08-31"
    },
    "filterByExpectedCloseDate": null,
    "filters": [],
    "search": "",
    "searchBy": "lead",
    "page": 1,
    "limit": 25,
    "columnId": 58,
    "onlyArchived": false
}'
```

#### Example: sweep an entire column

```javascript
async function listarColunaCompleta(funnelId, columnId, apitoken, instancia) {
  const base = `https://sprinthub-api-master.sprinthub.app/crm/opportunities/${funnelId}?i=${instancia}&apitoken=${apitoken}`;
  const limit = 50;
  let page = 1;
  const todas = [];

  while (true) {
    const res = await fetch(base, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        filterByUsers: [],
        filterByStatus: ["open"],
        filterByCreateDate: null,
        filterByExpectedCloseDate: null,
        filters: [],
        search: "",
        searchBy: "lead",
        page,
        limit,
        columnId,
        onlyArchived: false,
      }),
    });

    const pagina = await res.json();
    const itens = Array.isArray(pagina) ? pagina : pagina.opportunities || [];

    todas.push(...itens);
    if (itens.length < limit) break;
    page++;
  }

  return todas;
}
```

To sweep the entire funnel stage by stage, run the function above in a loop over the funnel column IDs. This is the recommended way to export large volumes: each column is a smaller and more stable sequence than a single pagination over the whole funnel, which shifts whenever a card changes stage during the sweep.

***

### Responses

#### 200 OK

```json
[
    {
        "id": 4533,
        "title": "Test Opportunity",
        "value": "1.00",
        "crm_column": 58,
        "lead_id": 8473,
        "sequence": 2
        ...
    }
]
```

Each item brings `crm_column` the same as `columnId` requested, when the column filter is used.

#### 400 Bad Request

```json
{
    "error": "Invalid request"
}
```

Malformed body or `funnelId` nonexistent.

#### 401 Unauthorized

```json
{
    "error": "Unauthorized"
}
```

`apitoken` missing, invalid, or lacking permission for the requested funnel.

***

### Best practices

* Use `limit` between 25 and 50 to feed interfaces. Higher values increase response time and the risk of timeout.
* When reapplying filters, always restart `page` at `1`.
* Prefer paginating by `columnId` when the goal is to reproduce the Kanban or export the funnel in a predictable way.
* Send all body fields, even the disabled ones (`[]` or `null`). This keeps the payload compatible with the interface and makes debugging easier by comparing it with browser requests.
* The results always respect the API key permissions. Two different keys may receive different counts for the same filters.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.sprinthub.com/en/topics/api/api-list-funnel-opportunities-with-filters-and-pagination.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
