> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.athenaintel.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.athenaintel.com/_mcp/server.

# Search emails

GET https://api.athenaintel.com/api/v0/tools/email/search

Search the caller's connected Gmail or Outlook mailbox.

Results come from the connected account the caller can access in their
current workspace (the default account unless `catalog_id` names another).
Unsent drafts are included and flagged with `is_draft`.

Reference: https://docs.athenaintel.com/api-reference/tools/email/search

## Authentication

- `X-API-KEY` header (required) — API Key authentication via header

## Request

### Query parameters

- `query` (string, required) — Search query. Gmail operators (`from:`, `to:`, `subject:`, `has:attachment`, `newer_than:7d`, `-term`, …) are accepted for both providers; operators with no Outlook equivalent are dropped and reported in `ignored_operators`. Use `in:drafts` to search only unsent drafts.
- `catalog_id` (string, optional, nullable) — Connected email account to use, as the catalog asset id returned by the Athena UI or the assets API. Defaults to the caller's default email account. An id that is not one of the caller's own connected accounts in the current workspace is a 404.
- `limit` (integer, optional, default: 20) — Maximum number of results (1-50).

## Response

### 200

Successful Response

- `catalog_id` (string, required) — The connected account that was searched.
- `count` (integer, required) — Number of results returned.
- `provider` (string, required) — Account provider as Athena reports it: `gmail` or `outlook` for accounts connected through the Integrations page; `google` or `microsoft365` for directly-connected accounts.
- `query` (string, required) — The query as executed.
- `draft_count` (integer, optional, default: 0) — How many of the results are unsent drafts.
- `ignored_operators` (list of string, optional, nullable) — Gmail search operators that have no Outlook equivalent and were dropped from the query. Outlook accounts only.
- `note` (string, optional, nullable) — Human-readable caveats about the results, when there are any.
- `results` (list of EmailSearchResultOut, optional) — Matching messages, newest first.

## Errors

### 400 Bad Request Error

The request cannot be carried out as written (`detail.code` = `EMAIL_REQUEST_REJECTED`): an invalid recipient address, or an account that is read-only in Athena (a Microsoft 365 shared mailbox).

- `any`

### 403 Forbidden Error

The caller has no workspace; the email toolkit is disabled in this environment (`detail.code` = `ENVIRONMENT_FEATURE_DISABLED`); the tool is disabled for the workspace in its Tool Registry (`TOOL_NOT_ENABLED`); or the connected account is denied by its provider (for example a Microsoft 365 or Google Workspace connection that has not been granted write permission) or its connector is not enabled for the workspace (`EMAIL_PROVIDER_ACCESS_DENIED`, `EMAIL_CONNECTOR_NOT_ENABLED`).

- `any`

### 404 Not Found Error

No Gmail or Outlook account is connected (`detail.code` = `EMAIL_ACCOUNT_NOT_CONNECTED`); the requested `catalog_id` is not an account the caller can use (`EMAIL_ACCOUNT_NOT_FOUND`); or the provider has no such message or event (`PROVIDER_RESOURCE_NOT_FOUND`).

- `any`

### 422 Unprocessable Entity Error

Validation Error

- `detail` (list of ValidationError, optional)

### 429 Too Many Requests Error

The connected account is temporarily rate-limited by its provider (`detail.code` = `EMAIL_PROVIDER_RATE_LIMITED`). `Retry-After` is set when the provider communicated a wait; retrying earlier extends the lockout.

- `any`

### 502 Bad Gateway Error

The email provider rejected or failed the request (`detail.code` = `EMAIL_PROVIDER_ERROR`); `detail.message` carries the provider's status and `detail.provider_status` its code.

- `any`

## Types

### EmailSearchResultOut

One message matched by an email search.

- `id` (string, required) — Provider message id (Gmail message id, or Microsoft Graph message id for Outlook). This — never an Athena asset id — is what `reply_to_message_id` accepts.
- `conversation_id` (string, optional, nullable) — Microsoft Graph conversation id. Outlook accounts only.
- `date` (string, optional, default: ) — Sent or received timestamp, in the provider's format.
- `draft_id` (string, optional, nullable) — Provider draft id, when the provider exposes one for a draft.
- `from` (string, optional, default: ) — Sender, as the provider reports it.
- `is_draft` (boolean, optional, default: false) — True when the message is an unsent draft sitting in the mailbox. Drafts are never sent or received mail and cannot be replied to.
- `snippet` (string, optional, default: ) — Short plain-text preview.
- `subject` (string, optional, default: ) — Subject line.
- `thread_id` (string, optional, nullable) — Gmail thread id. Gmail accounts only.
- `to` (string, optional, default: ) — Recipients, comma-separated as the provider reports them.

### ValidationError

- `loc` (list of ValidationErrorLocItems, required)
- `msg` (string, required)
- `type` (string, required)
- `ctx` (ValidationErrorCtx, optional)
- `input` (any, optional)

### ValidationErrorLocItems

### ValidationErrorCtx

## Examples

**Response**

```json
{
  "catalog_id": "string",
  "count": 1,
  "provider": "string",
  "query": "string",
  "draft_count": 0,
  "ignored_operators": [
    "string"
  ],
  "note": "string",
  "results": [
    {
      "id": "string",
      "conversation_id": "string",
      "date": "",
      "draft_id": "string",
      "from": "",
      "is_draft": false,
      "snippet": "",
      "subject": "",
      "thread_id": "string",
      "to": ""
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api.athenaintel.com/api/v0/tools/email/search"

querystring = {"query":"query"}

headers = {"X-API-KEY": "<apiKey>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript
const url = 'https://api.athenaintel.com/api/v0/tools/email/search?query=query';
const options = {method: 'GET', headers: {'X-API-KEY': '<apiKey>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.athenaintel.com/api/v0/tools/email/search?query=query"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("X-API-KEY", "<apiKey>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.athenaintel.com/api/v0/tools/email/search?query=query")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["X-API-KEY"] = '<apiKey>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.athenaintel.com/api/v0/tools/email/search?query=query")
  .header("X-API-KEY", "<apiKey>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.athenaintel.com/api/v0/tools/email/search?query=query', [
  'headers' => [
    'X-API-KEY' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.athenaintel.com/api/v0/tools/email/search?query=query");
var request = new RestRequest(Method.GET);
request.AddHeader("X-API-KEY", "<apiKey>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["X-API-KEY": "<apiKey>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.athenaintel.com/api/v0/tools/email/search?query=query")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```