How Caching Works

Every connected LinkedIn account has a finite action budget. A read Curviate answers from your tenant's own store spends none of it, so the same profile fetched twice in an hour costs one call.

The store, in one paragraph

When a read reaches LinkedIn, Curviate keeps the result in your tenant's store, stamped with when it was observed. The next read of that resource compares the stamp against its kind's freshness threshold: inside it, you get the stored copy for no budget; outside it, Curviate fetches again and restamps. The store is tenant scoped: one workspace never reads another's records.

Two parameters

Every entity read accepts mode and max_age as query parameters.

max_age is the real mechanism, in seconds. Three of the four modes are presets over it.

ModeMeaning
autoDefault. Serves a stored copy within the resource's threshold, otherwise fetches.
liveAlways reaches LinkedIn. Equivalent to max_age=0.
refillServes a stored copy at any age; fetches only when none is stored yet.
cache_onlyNever fetches. Returns NOT_STORED when nothing is stored.

A max_age you supply yourself outranks the auto, live and refill presets in both directions, so you can ask for something fresher or staler than the configured threshold. The largest value accepted is 31536000 (one year).

cache_only is the exception: not a max_age value but a guarantee that this read never reaches LinkedIn. So max_age alongside cache_only is refused rather than ignored; a request can never quietly become the platform call the mode exists to prevent.

# Default. Stored copy if it is fresh enough, otherwise a live fetch.
curl -H "Authorization: Bearer $CURVIATE_API_KEY" \
  "https://api.curviate.com/v1/acc_YOUR_ACCOUNT_ID/profiles/some-member"
 
# Force a fetch.
curl ... "https://api.curviate.com/v1/acc_YOUR_ACCOUNT_ID/profiles/some-member?mode=live"
 
# Accept anything up to an hour old.
curl ... "https://api.curviate.com/v1/acc_YOUR_ACCOUNT_ID/profiles/some-member?max_age=3600"
 
# Spend no budget under any circumstances.
curl ... "https://api.curviate.com/v1/acc_YOUR_ACCOUNT_ID/profiles/some-member?mode=cache_only"

Default freshness thresholds

These are configuration, not constants: they can change without a deploy, so treat them as current defaults, not a contract.

ResourceDefault threshold
Person12 hours
Company3 days
Post12 hours
Comment12 hours
Job12 hours
Group14 days
Chat12 hours
Message12 hours

Resources decay differently. A company's core record is stable for days; a post's reaction and comment counters move within hours, so a post refreshes on a profile's schedule even though its body rarely changes.

A resource kind with no configured threshold is treated as zero, meaning every read of that kind reaches LinkedIn. Curviate fails toward spending budget rather than serving stale data as current.

Knowing how old an answer is

Every entity read carries two fields:

  • observed_at: when the data was actually seen on LinkedIn.
  • source: whether this response came from the store or from a live fetch.

Over REST, a read answered purely from the store also carries an Age header. MCP has no headers, so an MCP client reads the same fact from observed_at and source.

{
  "id": "...",
  "observed_at": "2026-09-18T08:14:02Z",
  "source": "store"
}

When to reach for each mode

  • Leave it on auto for almost everything. It protects your budget without letting answers go quietly stale.
  • Use live when you are about to act on the result and correctness matters more than budget: checking whether an invitation was accepted before sending a follow-up, or reading a headline you are about to personalise a message with.
  • Use refill for bulk enrichment, where having a record at all matters more than how fresh it is. It fetches once per resource and then stops spending.
  • Use cache_only when your agent must not act on the account at all, for example on an untrusted or exploratory code path. It is the only unconditional promise that a read will not touch LinkedIn. Handle NOT_STORED as a normal outcome rather than an error. A 502 under cache_only means the store could not be read, not that nothing is stored: nothing reached LinkedIn, and the read is worth retrying.

A stored answer can carry less than a live one

Curviate strips some fields before writing, so they cannot be served from the store. A response with source set to the store may therefore omit a field that the same read returns when it goes live.

If your code depends on a field that is not present, re-read with mode=live rather than treating the absence as the resource not having a value.

Related