Skip to content

Request and Response Metadata

Metadata allows you to control caching behavior and inspect cache operations. Hishel supports metadata on both requests (to control caching) and responses (to inspect what happened).

All metadata fields are prefixed with hishel_ to avoid collisions with user data.


Request Metadata

Request metadata controls how Hishel caches the request and its response. You can set metadata using:

  • httpx: extensions parameter (recommended) or X-Hishel-* headers
  • requests: X-Hishel-* headers

httpx supports both methods

While httpx supports both extensions and headers, using extensions is recommended as it provides better type safety and doesn't pollute HTTP headers sent to the server.

hishel_ttl

Type: float | None

Description: Sets a custom time-to-live (TTL) for the cached response. After the specified number of seconds, the cached response will be considered expired and removed during cleanup.

Use Cases:

  • Override default TTL for specific endpoints
  • Set shorter TTL for frequently changing data
  • Set longer TTL for static resources

Default: Storage's default_ttl setting

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Cache this response for 1 hour using extensions (recommended)
response = client.get(
    "https://api.example.com/data",
    extensions={"hishel_ttl": 3600}
)
from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Cache this response for 1 hour using headers
response = client.get(
    "https://api.example.com/data",
    headers={"X-Hishel-Ttl": "3600"}
)
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

# Cache this response for 1 hour
response = session.get(
    "https://api.example.com/data",
    headers={"X-Hishel-Ttl": "3600"}
)

hishel_refresh_ttl_on_access

Type: bool | None

Description: When True, accessing a cached entry resets its TTL, keeping frequently accessed entries fresh. When False, the TTL countdown starts from the original storage time and is not affected by subsequent accesses.

Use Cases:

  • Keep popular content cached longer (sliding expiration)
  • Ensure rarely accessed content expires on schedule (fixed expiration)

Default: Storage's refresh_ttl_on_access setting (typically True)

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Enable sliding expiration - each access resets the timer
response = client.get(
    "https://api.example.com/user/profile",
    extensions={"hishel_refresh_ttl_on_access": True}
)
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

# Enable sliding expiration - each access resets the timer
response = session.get(
    "https://api.example.com/user/profile",
    headers={"X-Hishel-Refresh-Ttl-On-Access": "true"}
)

hishel_spec_ignore

Type: bool | None

Description: When True, Hishel ignores RFC 9111 caching rules and caches the response regardless of Cache-Control, Expires, or other standard headers. The response will be stored even if it would normally be uncacheable.

Use Cases:

  • Force caching of responses with no-store or no-cache
  • Cache responses without proper cache headers
  • Override server caching directives for testing

Default: False (follow RFC 9111 specification)

Warning

Use with caution. Ignoring the specification may cache sensitive data or stale content.

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Force caching even if server says not to
response = client.get(
    "https://api.example.com/dynamic-content",
    extensions={"hishel_spec_ignore": True}
)
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

# Force caching even if server says not to
response = session.get(
    "https://api.example.com/dynamic-content",
    headers={"X-Hishel-Spec-Ignore": "true"}
)

hishel_body_key

Type: bool | None

Description: When True, includes the request body in cache key generation. This allows caching different responses for the same URL but with different request bodies, which is essential for POST requests and GraphQL queries.

Use Cases:

  • Cache POST requests with different payloads
  • Cache GraphQL queries (different queries to same endpoint)
  • Cache search requests with different parameters in body

Default: False (body not included in cache key)

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Cache POST request based on body content
response = client.post(
    "https://api.example.com/search",
    json={"query": "python"},
    extensions={"hishel_body_key": True}
)
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

# Cache POST request based on body content
response = session.post(
    "https://api.example.com/search",
    json={"query": "python"},
    headers={"X-Hishel-Body-Key": "true"}
)

Response Metadata

Response metadata provides information about cache operations that occurred. These fields are read-only and set by Hishel.

hishel_from_cache

Type: bool | None

Description: Indicates whether the response was served from cache (True) or fetched from the origin server (False).

Use Cases:

  • Monitor cache hit rates
  • Debug caching behavior
  • Log cache performance
  • Conditional logic based on cache status

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

response = client.get("https://api.example.com/data")

# Check if response came from cache
if response.extensions.get("hishel_from_cache"):
    print("✓ Cache hit")
else:
    print("✗ Cache miss - fetched from origin")
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

response = session.get("https://api.example.com/data")

# Check if response came from cache
if response.headers.get("X-Hishel-From-Cache") == "true":
    print("✓ Cache hit")
else:
    print("✗ Cache miss - fetched from origin")

hishel_revalidated

Type: bool | None

Description: Indicates whether a stale cached response was revalidated with the origin server. When True, the response was in cache but required validation (typically resulting in a 304 Not Modified response).

Use Cases:

  • Monitor revalidation frequency
  • Debug cache freshness logic
  • Track conditional request behavior
  • Optimize cache TTL settings

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

response = client.get("https://api.example.com/data")

# Check if cached response was revalidated
if response.extensions.get("hishel_revalidated"):
    print("Response was revalidated (304 Not Modified)")
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

response = session.get("https://api.example.com/data")

# Check if cached response was revalidated
if response.headers.get("X-Hishel-Revalidated") == "true":
    print("Response was revalidated (304 Not Modified)")

hishel_spec_ignored

Type: bool | None

Description: Indicates whether RFC 9111 caching specification was ignored for this response. When True, the response was cached despite having directives that would normally prevent caching (like Cache-Control: no-store).

Use Cases:

  • Verify hishel_spec_ignore worked as expected
  • Audit which responses bypass standard caching rules
  • Debug forced caching behavior

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

# Force cache a response
response = client.get(
    "https://api.example.com/no-cache-endpoint",
    extensions={"hishel_spec_ignore": True}
)

# Verify spec was ignored
if response.extensions.get("hishel_spec_ignored"):
    print("✓ Caching rules were bypassed as requested")
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

# Force cache a response
response = session.get(
    "https://api.example.com/no-cache-endpoint",
    headers={"X-Hishel-Spec-Ignore": "true"}
)

# Verify spec was ignored
if response.headers.get("X-Hishel-Spec-Ignored") == "true":
    print("✓ Caching rules were bypassed as requested")

hishel_stored

Type: bool | None

Description: Indicates whether the response was successfully stored in cache. When True, the response met all caching requirements and was saved. When False, the response was not cacheable (e.g., due to Cache-Control: no-store).

Use Cases:

  • Verify responses are being cached
  • Debug why responses aren't cached
  • Monitor cache storage success rate
  • Validate caching configuration

Example:

from hishel.httpx import SyncCacheClient

client = SyncCacheClient()

response = client.get("https://api.example.com/data")

# Check if response was cached
if response.extensions.get("hishel_stored"):
    print("✓ Response stored in cache")
else:
    print("✗ Response not cached")
import requests
from hishel.requests import CacheAdapter

session = requests.Session()
session.mount("http://", CacheAdapter())
session.mount("https://", CacheAdapter())

response = session.get("https://api.example.com/data")

# Check if response was cached
if response.headers.get("X-Hishel-Stored") == "true":
    print("✓ Response stored in cache")
else:
    print("✗ Response not cached")

hishel_created_at

Type: float | None

Description: POSIX timestamp (seconds since the epoch) indicating when the response entry was created in the cache. This value is set by Hishel when the response is stored and can be used with hishel_ttl to compute remaining freshness.

Use Cases:

  • Determine when an entry was cached for logging or debugging.
  • Compute remaining TTL: remaining = hishel_ttl - (now - hishel_created_at).

Example (httpx extensions):

created = response.extensions.get("hishel_created_at")
if created:
    print("Cached at:", created)

Example (requests headers):

created = response.headers.get("X-Hishel-Created-At")
if created:
    print("Cached at:", created)