# /arstechnica/articles/search

`POST /api/arstechnica/articles/search`

Price: 10 credits

Search and browse the Ars Technica archive back to 1998. Combine a full-text query with section, topic-tag, editorial-series and author filters, publish and update date ranges and the sort order. Each result carries headline, dek, publish and update timestamps, the credited writer, the resolved taxonomy terms and the featured image.

## How to use it

List Ars Technica articles. Set count for how many to return. Filter with keyword (full text, narrowed to the headline, the body or the excerpt with keyword_fields), categories/tags/series/feature_series and their _exclude twins (slugs from arstechnica/categories/search), authors (slugs like 'ryanwhitwam' or numeric ids), articles (fetch specific articles by id, slug or URL in one call) and their _exclude twins, and the published_after/published_before/updated_after/updated_before ISO 8601 UTC ranges, which line up with the published_at and updated_at values in the result. authors_exclude and articles_exclude narrow their own include list when both are sent, so authors=['a','b'] with authors_exclude=['b'] returns only a, and excluding everything you included returns nothing. taxonomy_relation switches the taxonomy filters between AND and OR. sort accepts date, modified, relevance (only meaningful together with keyword), title, slug, id or author; order is asc or desc. Each item has id, article_title, alias, web_url, excerpt, published_at, updated_at, author, image, image_id and taxonomy terms — pass id or web_url to arstechnica/articles for the full body, the kicker and the comment count.

## Parameters

- `access-token` (string, required)

## Request body

- `timeout` (integer) — Max scrapping execution timeout (in seconds) (default: 300; min: 20; max: 1500)
- `articles` (array, nullable) — Only these articles, by numeric id, slug or article URL (examples: ["2167613","how-we-tested-the-new-mac-mini"]; maxItems: 500)
- `articles_exclude` (array, nullable) — Drop these articles, by numeric id, slug or article URL (maxItems: 500)
- `keyword` (string, nullable) — Full-text query matched against article title and body (examples: "openai")
- `keyword_fields` (array, nullable) — Restrict the keyword match to these parts of the article (one of: "post_title", "post_content", "post_excerpt"; examples: ["post_title"])
- `categories` (array, nullable) — Only articles filed under these section slugs or ids (examples: ["ai"])
- `categories_exclude` (array, nullable) — Drop articles filed under these section slugs or ids
- `tags` (array, nullable) — Only articles carrying these topic tag slugs or ids (examples: ["openai"])
- `tags_exclude` (array, nullable) — Drop articles carrying these topic tag slugs or ids
- `series` (array, nullable) — Only articles belonging to these editorial series slugs or ids
- `series_exclude` (array, nullable) — Drop articles belonging to these editorial series slugs or ids
- `feature_series` (array, nullable) — Only articles belonging to these long-form feature series
- `feature_series_exclude` (array, nullable) — Drop articles belonging to these long-form feature series
- `authors` (array, nullable) — Only articles written by these author slugs or ids (examples: ["ryanwhitwam"]; maxItems: 500)
- `authors_exclude` (array, nullable) — Drop articles written by these author slugs or ids (maxItems: 500)
- `published_after` (string, nullable) — Only articles whose published_at is at or after this ISO 8601 UTC moment (examples: "2020-01-01T00:00:00")
- `published_before` (string, nullable) — Only articles whose published_at is before this ISO 8601 UTC moment (examples: "2021-01-01T00:00:00")
- `updated_after` (string, nullable) — Only articles whose updated_at is at or after this ISO 8601 UTC moment
- `updated_before` (string, nullable) — Only articles whose updated_at is before this ISO 8601 UTC moment
- `taxonomy_relation` (string, nullable) — Combine the section, tag and series filters with AND or OR (one of: "AND", "OR")
- `sort` (string, nullable) — Field the results are ordered by (one of: "date", "modified", "relevance", "title", "slug", "id", "author")
- `order` (string, nullable) — Sort direction (one of: "asc", "desc")
- `skip` (integer) — Drop this many of the newest matches before collecting (default: 0; min: 0)
- `count` (integer, required) — Max number of articles to return (min: 1)

## Response

### 200 — Successful Response

- `@type` (string) (default: "ArstechnicaArticleCard")
- `id` (integer, required)
- `article_title` (string, required)
- `alias` (string, required)
- `web_url` (string, required)
- `published_at` (integer, nullable)
- `updated_at` (integer, nullable)
- `excerpt` (string, nullable)
- `post_format` (string, nullable)
- `image` (string, nullable)
- `image_id` (integer, nullable)
- `apple_news_url` (string, nullable)
- `wp_author_id` (integer, nullable)
- `author` (object, nullable)
  - `@type` (string) (default: "ArstechnicaArticleAuthor")
  - `id` (integer, required)
  - `name` (string, required)
  - `alias` (string, nullable)
  - `web_url` (string, nullable)
  - `description` (string, nullable)
  - `homepage_url` (string, nullable)
- `categories` (array) (default: [])
  - `@type` (string) (default: "ArstechnicaTerm")
  - `id` (integer, required)
  - `name` (string, required)
  - `alias` (string, nullable)
  - `taxonomy` (string, nullable)
  - `web_url` (string, nullable)
  - `description` (string, nullable)
  - `article_count` (integer, nullable)
- `tags` (array) (default: [])
  - `@type` (string) (default: "ArstechnicaTerm")
  - `id` (integer, required)
  - `name` (string, required)
  - `alias` (string, nullable)
  - `taxonomy` (string, nullable)
  - `web_url` (string, nullable)
  - `description` (string, nullable)
  - `article_count` (integer, nullable)
- `feature_series` (array) (default: [])
  - `@type` (string) (default: "ArstechnicaTerm")
  - `id` (integer, required)
  - `name` (string, required)
  - `alias` (string, nullable)
  - `taxonomy` (string, nullable)
  - `web_url` (string, nullable)
  - `description` (string, nullable)
  - `article_count` (integer, nullable)
- `series` (array) (default: [])
  - `@type` (string) (default: "ArstechnicaTerm")
  - `id` (integer, required)
  - `name` (string, required)
  - `alias` (string, nullable)
  - `taxonomy` (string, nullable)
  - `web_url` (string, nullable)
  - `description` (string, nullable)
  - `article_count` (integer, nullable)

## Errors

### 422 — Validation Error

The request body did not validate

What to do: Check the fields against this schema. A URN with the wrong prefix is the most common cause.

- `detail` (array)
  - `loc` (array, required)
  - `msg` (string, required)
  - `type` (string, required)
  - `input` (any)
  - `ctx` (object)

### 408

The request ran past its time limit

What to do: Raise `timeout` in the request body, up to the maximum this endpoint documents. Lowering `count` or turning off the `with_*` flags also helps, because less work finishes sooner.

### 412

The entity was not found, or a precondition failed

What to do: Retrying will not help: either the entity does not exist, or the input points at a different one.

### 429

Too many requests: a rate limit or a usage window is exhausted

What to do: When the response carries an X-Retry-After header, wait that many seconds and retry: the same number is in the body as `detail.retry_after`, and the limit clears once that window passes. The message in the body names the limit that was hit.

### 500

Something broke on our side

What to do: Retrying will not help. If it keeps happening, send us the X-Request-ID from the response headers.

### 529

Rate limit reached, or the endpoint is overloaded

What to do: Wait at least 30 seconds, then retry.

## Response envelope

Success: Array of objects (may be empty if no results)

Error: Error may coexist with partial results if it occurs mid-execution. Check X-Error header and status code.

Every response carries these headers:

- `X-Error` — Error message text (present only on error)
- `X-Request-ID` — Unique request identifier
- `X-Execution-Time` — Execution time in seconds
- `X-Result-Count` — How many records the body carries. 0 means an empty result, which is a normal answer and not by itself an error. A non-zero count can come back together with X-Error when the failure happened partway through — read this header and X-Error independently.
- `X-Total-Available-Results` — How many records exist for this query, when the endpoint can say. On a `dry_run` request this is the answer and the body is empty. It saturates: the endpoint's documented maximum means 'at least that many', any smaller number is exact.
- `X-Warning` — Present when the request body carried keys this endpoint does not document. They were ignored, so any filter you meant to apply through them did not apply. Check the spelling against this schema and retry.
- `X-Retry-After` — Seconds to wait before retrying. Present only on 429.

