# Search HackerNews (https://snitchfeed.com/docs/api/hackernews/search)

**Authentication:** All requests require `Authorization: Bearer <api_key>` header. Obtain your API key from the SnitchFeed platform under Integrations → API Keys.

## `POST /data/hackernews/search`

**Full URL:** `https://api.snitchfeed.com/v1/data/hackernews/search`

**Summary:** Search HackerNews

Search Hacker News stories and comments by keyword, with author, points and comment-count qualifiers. Costs 2 credits per successful request.

Results come from Algolia's Hacker News index.

Query syntax

`query` supports HN's native search qualifiers in addition to plain keywords: `author:USERNAME` / `by:USERNAME` (filter by author), `story:ID` (filter to one story's items), `points>N` / `points<N`, `comments>N` / `comments<N`, `"exact phrase"`, and `-word` (exclude). A query can be qualifiers-only with no keywords, e.g. `author:pg points>500`.

Credits

Each **successful** response deducts **2 credits** from your organization. Requests that fail (4xx / 5xx) are not billed.

See the [credits documentation](https://snitchfeed.com/docs/api/credits) for the full rate table.

### Request Body

**Content-Type:** `application/json`

#### Fields

Request body for `POST /data/hackernews/search`.

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `query` | string | ✓ | Search keywords for HackerNews stories/comments. Also supports HN's native search qualifiers: `author:USERNAME` or `by:USERNAME` (filter by author), `story:ID` (filter to one story's items), `points>N` / `points<N` (filter by points), `comments>N` / `comments<N` (filter by comment count), `"exact phrase"`, and `-word` (exclude a word). Can be qualifiers-only with no keywords, e.g. "author:pg points>500". (minLength: 1, maxLength: 200) |
| `tags` | "story", "comment"[] |  | Restrict to stories, comments, or both (default both). |
| `page` | integer |  | Zero-indexed page number for pagination. (min: 0) |
| `hitsPerPage` | integer |  | Results per page (capped at 100). (min: 1, max: 100) |

### Responses

#### `200`: Search results

#### Response fields

Successful `POST /data/hackernews/search` response.

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `posts` | object[] | ✓ | Matching items for this page. |
| `meta` | object | ✓ | Pagination metadata for HackerNews search. |
| `returnedCount` | integer | ✓ | Number of items returned in this response. (min: 0) |
| `hasMore` | boolean | ✓ | Whether more results exist beyond this page. |
| `nextPage` | integer |  | Pass as `page` in the next request to fetch the next page. |

#### `400`: Validation or bad request

#### Response fields

Validation error

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `error` | object | ✓ |  |
| `code` | "validation_error" | ✓ |  |
| `message` | string | ✓ |  |
| `issues` | object[] | ✓ |  |

**Example:**
```json
{
  "requestId": "01HXYZABCDEF",
  "error": {
    "code": "validation_error",
    "message": "query: String must contain at least 1 character(s)",
    "issues": [
      {
        "path": "query",
        "message": "String must contain at least 1 character(s)"
      }
    ]
  }
}
```

#### `401`: Unauthorized

#### Response fields

Unauthorized

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `error` | object | ✓ |  |
| `code` | "unauthorized" | ✓ |  |
| `message` | string | ✓ |  |

**Example:**
```json
{
  "requestId": "01HXYZABCDEF",
  "error": {
    "code": "unauthorized",
    "message": "Invalid or revoked API key."
  }
}
```

#### `403`: Forbidden: the credential lacks the `search:read` scope, the organization's plan does not include API access (API keys only), or the authorizing user's role lacks the required permission.

#### Response fields

Forbidden — the API key lacks a required scope, or its owner's role lacks the underlying permission.

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `error` | object | ✓ |  |
| `code` | "forbidden" | ✓ |  |
| `message` | string | ✓ |  |

**Example:**
```json
{
  "requestId": "01HXYZABCDEF",
  "error": {
    "code": "forbidden",
    "message": "This credential does not have the \"search:read\" scope."
  }
}
```

#### `429`: Rate limited (20 requests per minute per surface) or too many concurrent in-flight requests (max 2 per organization)

#### Response fields

Rate limited or too many concurrent in-flight requests

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `error` | object | ✓ |  |
| `code` | "rate_limited" | ✓ |  |
| `message` | string | ✓ |  |
| `retryAfter` | integer | ✓ | Seconds until the rate limit resets (same value as the `Retry-After` response header). |

**Example:**
```json
{
  "requestId": "01HXYZABCDEF",
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded for linkedin. Try again later.",
    "retryAfter": 42
  }
}
```

#### `502`: Upstream error

#### Response fields

Upstream error

| Field | Type | Required | Description |
|-------|------|:--------:|-------------|
| `requestId` | string | ✓ | Server-generated id for this request (logging / support). |
| `error` | object | ✓ |  |
| `code` | "upstream_error" | ✓ |  |
| `message` | string | ✓ |  |

**Example:**
```json
{
  "requestId": "01HXYZABCDEF",
  "error": {
    "code": "upstream_error",
    "message": "Upstream request failed."
  }
}
```
