> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oathnet.org/llms.txt
> Use this file to discover all available pages before exploring further.

# File Search Guide

> Run asynchronous searches across victim file contents and poll job snapshots

## Overview

File search is an async workflow:

1. choose the logs you want to inspect
2. create a search job with an expression and matching mode
3. poll the job until it reaches a terminal status
4. read matches from the final raw job snapshot

This is different from victim file download. File search tells you whether a
pattern exists across selected logs; victim file download fetches one known file
after you have a manifest path or file ID.

<Note>
  Both file-search endpoints return raw job snapshots. Do not expect a top-level `success` or `data` wrapper.
</Note>

## Create A Job

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://oathnet.org/api/service/v2/file-search" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "expression": "api[_-]?key",
      "search_mode": "regex",
      "log_ids": ["vic_002_gamer_dragonslayer"],
      "include_matches": true,
      "context_lines": 2,
      "max_matches": 50
    }'
  ```

  ```python Python theme={null}
  import requests
  import time

  create_response = requests.post(
      "https://oathnet.org/api/service/v2/file-search",
      headers={
          "x-api-key": "YOUR_API_KEY",
          "Content-Type": "application/json",
      },
      json={
          "expression": "api[_-]?key",
          "search_mode": "regex",
          "log_ids": ["vic_002_gamer_dragonslayer"],
          "include_matches": True,
          "context_lines": 2,
          "max_matches": 50,
      },
  )

  job = create_response.json()
  job_id = job["job_id"]

  while True:
      snapshot = requests.get(
          f"https://oathnet.org/api/service/v2/file-search/{job_id}",
          headers={"x-api-key": "YOUR_API_KEY"},
      ).json()

      if snapshot["status"] == "completed":
          break

      time.sleep(snapshot.get("next_poll_after_ms", 1000) / 1000)

  for match in snapshot.get("matches", []):
      print(match["relative_path"], match["line_number"], match["match_text"])
  ```
</CodeGroup>

```json Create Response Example theme={null}
{
  "job_id": "fs_job_1704067200_abc123",
  "status": "queued",
  "created_at": "2024-01-01T10:00:00Z",
  "next_poll_after_ms": 500,
  "limits": {
    "max_matches": 100,
    "max_log_ids": 10,
    "max_context_lines": 5,
    "job_ttl_seconds": 3600
  }
}
```

```json Completed Response Example theme={null}
{
  "job_id": "fs_job_1704067200_abc123",
  "status": "completed",
  "created_at": "2024-01-01T10:00:00Z",
  "completed_at": "2024-01-01T10:00:05Z",
  "summary": {
    "files_scanned": 45,
    "files_total": 45,
    "files_matched": 3,
    "matches": 7,
    "bytes_scanned": 125000,
    "duration_ms": 4823,
    "budget_exceeded": false,
    "truncated": false,
    "timeouts": 0
  },
  "matches": [
    {
      "log_id": "vic_002_gamer_dragonslayer",
      "file_id": "config_env",
      "file_name": ".env",
      "relative_path": "configs/.env",
      "match_text": "API_KEY=sk-abc123def456",
      "line_number": 5
    }
  ]
}
```

## When To Use It

Use file search when you already know which victim logs matter and need to locate secrets, config files, wallet addresses, or other patterns inside raw files.

<CardGroup cols={2}>
  <Card title="Victim Workflows" icon="user" href="/guides/victims">
    Find the `log_id` values first
  </Card>

  <Card title="Stealer Search" icon="search" href="/guides/stealer-search">
    Pivot from credential results into logs and files
  </Card>

  <Card title="Export Jobs" icon="download" href="/guides/exports">
    Export larger result sets
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/overview">
    Use OpenAPI for exact job schema, limits, statuses, and playground requests
  </Card>
</CardGroup>
