# Search opportunities

Operation ID: `searchOpportunities`

Group: CRM › Opportunities

[Open in the interactive reference](https://docs.api.lensing.ai/lensing/v1/endpoints/searchOpportunities)

`POST /crm/opportunities/search`

Read-only: A read that takes its filters in the body. Changes nothing, and ignores Idempotency-Key.

Searches opportunities. Use this endpoint for criteria a query string cannot carry: opportunityName is free text, and every criterion is a list, so you can pass several values of one field at once. Criteria combine with AND, and a list within one criterion is an OR. For example, currencies ['USD', 'EUR'] matches either. The item schema is the same here, on GET crm/opportunities and on GET crm/opportunities/:id.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Api-Version` | header | string · v1 | yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version `v1`. |

## Request body

Required, `application/json`: searchOpportunitiesInput.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `filters` | object | no | The filters to apply. An opportunity must match every filter you send. Omit all of them to return every opportunity you can see. |
| `filters.id` | string | no | Limits results to the opportunity with this id. |
| `filters.opportunityNumber` | string | no | Limits results to the opportunity with this number, for example 'OP-1042'. The match is exact. |
| `filters.opportunityName` | string | no | Limits results to opportunities whose name contains this text. The match is case-insensitive. |
| `filters.entityIds` | array<string> | no | Limits results to opportunities owned by any of these legal entities. Omit or pass an empty array for every entity. |
| `filters.customerIds` | array<string> | no | Limits results to opportunities for any of these customers or prospects, matched against either. Omit or pass an empty array for every customer. |
| `filters.currencies` | array<string> | no | Limits results to opportunities in any of these currencies. Each is an ISO 4217 code, for example 'USD'. Omit or pass an empty array for every currency. |
| `filters.crmId` | string | no | Limits results to opportunities with this external system identifier. The match is exact. |
| `filters.createdDate` | object | no | Limits results by when the opportunity was created, in UTC. Both bounds are inclusive, and omitting one leaves that side open. |
| `filters.createdDate.start` | string | no | The earliest creation time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `filters.createdDate.end` | string | no | The latest creation time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `filters.lastModifiedDate` | object | no | Limits results by when the opportunity last changed, in UTC. Both bounds are inclusive, and omitting one leaves that side open. |
| `filters.lastModifiedDate.start` | string | no | The earliest change time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `filters.lastModifiedDate.end` | string | no | The latest change time to include, inclusive. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `page` | object | no | The page to return. Omit for the first page at the default size. |
| `page.cursor` | string | no | The cursor from the previous response's pagination.nextCursor. Omit on the first request. |
| `page.size` | number | no | The number of opportunities per page. Defaults to 50, and a value above 200 is treated as 200, not rejected. |

```json
{
  "filters": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "opportunityNumber": "<opportunityNumber>",
    "opportunityName": "<opportunityName>",
    "entityIds": [
      "<entityIds>"
    ],
    "customerIds": [
      "<customerIds>"
    ],
    "currencies": [
      "<currencies>"
    ],
    "crmId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "createdDate": {
      "start": "<start>",
      "end": "<end>"
    },
    "lastModifiedDate": {
      "start": "<start>",
      "end": "<end>"
    }
  },
  "page": {
    "cursor": "<cursor>",
    "size": 0
  }
}
```

## Response

`200` Success

Under `data` (searchOpportunitiesOutput):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array<object> | yes | The opportunities on this page, most recently created first. An empty array when nothing matches. |
| `items[].id` | string | yes | The unique identifier for the opportunity. |
| `items[].opportunityNumber` | string | no | The system-generated opportunity number, for example 'OP-1042'. Absent while none has been assigned. |
| `items[].entityId` | string | yes | The legal entity that owns the opportunity. |
| `items[].currency` | string | yes | The currency the opportunity is denominated in, and the currency of amount. Format ISO 4217, for example 'USD'. |
| `items[].customerId` | string | no | The identifier of the customer or prospect this opportunity is for. customerPath says which kind of record it is. Absent when it points at neither. See id on GET accounting/customers/:id for a customer, or GET crm/prospects/:id for a prospect. |
| `items[].customerPath` | string | no | Which kind of record customerId identifies. The value ends in 'Customer.id' when it is a customer and in 'Prospect.id' when it is a prospect. Absent when neither resolves. |
| `items[].endCustomerId` | string | no | The identifier of the end customer behind a partner customer. Absent unless one is on record. See id on GET accounting/customers/:id. |
| `items[].priceBookId` | string | yes | The identifier of the price book quotes on this opportunity are priced from. See id on GET accounting/price-books/:id. |
| `items[].stageId` | string | yes | The identifier of the stage the opportunity currently sits in: one of the stages on GET crm/opportunity-stages/:id, where :id is this opportunity's entityId. |
| `items[].opportunityName` | string | no | The free-text display name of the opportunity. Absent when none is set. |
| `items[].closeDate` | date | no | The expected close date of the deal. Format YYYY-MM-DD. Absent when none is set. |
| `items[].amount` | decimal | no | The expected deal amount, as a decimal in the opportunity's currency. Absent when none is set. |
| `items[].crmId` | string | no | The identifier of this opportunity in the external system it was imported from, for example a Salesforce record id. It is free text and not unique. Absent when none is set. |
| `items[].dataSource` | string | no | The system the record came from, for example 'Salesforce'. Records created in Lensing have the value 'Everest'. Absent when none is set. |
| `items[].lastModifiedDate` | date-time | no | The date and time the opportunity record last changed, in UTC. Format YYYY-MM-DDTHH:mm:ss.sssZ. |
| `items[].quotes` | array<object> | yes | Every quote linked to this opportunity. An empty array when it has none. |
| `items[].quotes[].quoteId` | string | yes | The identifier of the linked quote. See id on GET crm/quotes/:id. |
| `items[].quotes[].isPrimary` | boolean | yes | Whether this is a primary quote for the opportunity. It is not guaranteed to be true for exactly one quote. |
| `pagination` | object | yes | The pagination details for this page. |
| `pagination.nextCursor` | string | no | The cursor for the next page. Pass it back as page.cursor. Absent on the last page. |
| `pagination.hasMore` | boolean | yes | Whether another page follows. A full page may still be the last, so branch on this. |

```json
{
  "data": {
    "items": [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "opportunityNumber": "<opportunityNumber>",
        "entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "currency": "<currency>",
        "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "customerPath": "<customerPath>",
        "endCustomerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "priceBookId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "stageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "opportunityName": "<opportunityName>",
        "closeDate": "2026-01-15",
        "amount": "19.99",
        "crmId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "dataSource": "<dataSource>",
        "lastModifiedDate": "2026-01-15T09:30:00.000Z",
        "quotes": [
          {
            "quoteId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            "isPrimary": true
          }
        ]
      }
    ],
    "pagination": {
      "nextCursor": "<nextCursor>",
      "hasMore": true
    }
  },
  "diagnostics": [
    {
      "code": "<code>",
      "message": "<message>",
      "severity": "error",
      "path": [
        "<path>"
      ],
      "details": "<details>"
    }
  ]
}
```

## Statuses

- `400` — Invalid request or business failure
- `401` — The request carries no valid access token.
- `403` — Not authorized
- `404` — Not found
- `429` — Too many requests from this client address. The response has no body and no `Retry-After` header.
- `500` — Internal server error
