CormorantDocs
Menu

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 (3600 for "at most an hour old") or longer. With cache_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):

  1. Send the same request strings. Use the same query text and the same language, country, max_results and freshness for searches. Use the same URLs (preferably the final_url of the last run) for fetches. Avoid volatile parts in queries, such as today's date or a run id. Put those in project_ref instead, which is not part of the key.
  2. Accept the age you can live with. Set cache_ttl_s to the interval between runs (e.g. 604800 for weekly runs, 2592000 for monthly). Every item that is still in the cache then answers instantly, as a cache hit.
  3. Force freshness only where needed. Use cache_ttl_s: 0 on the few URLs that must be live (e.g. a news page), not on the whole job.
  4. Check the hit rate. Results and GET /v1/requests carry cached, and GET /v1/usage shows cache_hit_rate per kind and the search_cached / fetch_cached categories.

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.

View as Markdown