Caching and reuse across runs
Cormorant keeps a shared cache of search answers and fetched pages. A hit
answers in milliseconds, is billed at the cache price (cached: true, billing
category search_cached / fetch_cached) and is the same for every project. This
guide lists what is cached for how long, and how to get hits when you run
the same job again.
Cache classes#
| What | Fresh for (default) | Kept after that | Key |
|---|---|---|---|
Search answers (/v1/search, batch search) |
7 days | not deleted: older answers are reused when your cache_ttl_s accepts them |
normalised query (whitespace collapsed, lower-case) + language + country + max_results + freshness |
| HTML pages | 7 days | 30 days, for revalidation and longer cache_ttl_s |
normalised URL (see below) |
| PDFs | 30 days | 30 days | normalised URL |
Short pages: direct and the browser both gave less text than min_text_chars, and nothing blocked |
1 day | 30 days | normalised URL |
| SEC EDGAR | archives (filings, documents) 365 days; metadata 12 h; full-text search 1 h | 30 days | normalised URL |
The same defaults apply to every customer. The operator can change them
(SEARCH_CACHE_TTL_S, HTML_CACHE_TTL_S, PDF_CACHE_TTL_S,
SHORT_PAGE_CACHE_TTL_S).
Not cached:
- errors
- hard-paywall teasers (so open copies are looked for again next time)
- thin pages from a fetch that was blocked
- archived copies standing in for a 404 or a dead domain
- alternative-source copies (
tier: "source") - search answers where Brave failed after SearXNG returned too little
An expired HTML or PDF entry with an ETag or Last-Modified is
revalidated with a conditional request. If the origin answers 304 Not Modified, you get the cached copy (cached: true, revalidated: true), billed
as a cache hit, and it is fresh again.
How URLs are normalised#
The cache key of a fetch is the normalised URL:
- scheme and host are lower-cased, and the default port and the fragment are dropped
- tracking parameters are removed (
utm_*,gclid,fbclid,mc_*,pk_*, …) - the remaining query parameters are sorted, and percent-encoding is normalised
http://Example.com:80/a?b=2&a=1&utm_source=x#top and
http://example.com/a?a=1&b=2 are the same entry.
http and https, www. and the bare host, and a trailing / are
different URLs. When a fetch is redirected, the page is also cached under
the URL the redirects ended at. A later request for https://www.site.com/
then hits the entry that a request for http://site.com created. For the
most hits, send the URLs you got back as final_url.
cache_ttl_s: how old an answer you accept#
- Unset: the class's freshness above.
cache_ttl_s: 0: bypass the cache and fetch or search again. The new answer replaces the entry.- Any other value is the maximum age you accept, measured from when the
answer was retrieved. It can be shorter than the default (
3600for "at most an hour old") or longer. Withcache_ttl_s: 2592000(30 days), a re-run a month later still reuses last month's searches and pages that are still kept.
Cache hits must still satisfy the request: render, tiers, accept_pdf,
max_bytes and min_text_chars all apply. A page with less text than your
min_text_chars is fetched again, unless it was cached as a short page.
Reusing the cache across runs#
For a recurring enrichment run (the same companies every week or month):
- Send the same request strings. Use the same query text and the same
language,country,max_resultsandfreshnessfor searches. Use the same URLs (preferably thefinal_urlof the last run) for fetches. Avoid volatile parts in queries, such as today's date or a run id. Put those inproject_refinstead, which is not part of the key. - Accept the age you can live with. Set
cache_ttl_sto the interval between runs (e.g.604800for weekly runs,2592000for monthly). Every item that is still in the cache then answers instantly, as a cache hit. - Force freshness only where needed. Use
cache_ttl_s: 0on the few URLs that must be live (e.g. a news page), not on the whole job. - Check the hit rate. Results and
GET /v1/requestscarrycached, andGET /v1/usageshowscache_hit_rateper kind and thesearch_cached/fetch_cachedcategories.
A first run naturally has few or no hits. Every query and URL is new, unless another project asked the same thing within the TTL, because the cache is shared.