# The official FastAPI Redis SDK is now available

**Tagline:** News & Media | **Authors:** Tisho Mateev, Mirko Ortensi | **Categories:** Tech | **Published:** 2026-09-24 | **Updated:** 2026-09-24

FastAPI now has an official Redis integration. With the [fastapi-redis-sdk](https://github.com/redis/fastapi-redis-sdk), we worked closely with the FastAPI team on what a naturally integrated Redis experience should look like, following FastAPI’s dependency model instead of forcing Redis into patterns that feel foreign to the framework.

The result is a native way to use Redis in FastAPI, with connection management tied to the application lifecycle, sync and async dependencies out of the box, and caching that fits cleanly into dependency injection. It also brings practical HTTP caching behaviors and cache-control headers, so it goes beyond basic connectivity and makes common web patterns feel first-class.

The [fastapi-redis-sdk](https://github.com/redis/fastapi-redis-sdk) can be used with all FastAPI flavors, [self-managed](https://github.com/fastapi/fastapi), and [FastAPI Cloud](https://fastapicloud.com/), the new managed platform for deploying and operating FastAPI apps. FastAPI Cloud is now in [public beta](https://fastapicloud.com/blog/fastapi-cloud-public-beta/), and [Redis Cloud](https://redis.io/cloud/) is available there as a native integration, giving users a straightforward way to connect an existing Redis database, or create a free one directly from FastAPI Cloud dashboard. This delivers a shorter path to production services and reduces effort with the ability to support caching today and more use cases under development through the fastapi-redis-sdk officially supported SDK.

## Using the SDK

Installing the package is as easy as any other package available at PyPI:

```python
pip install fastapi-redis-sdk

```

Then to attach the SDK to the lifecycle of your app you need to call:

```python
from fastapi import FastAPI
from redis_fastapi import FastAPIRedis, cache

app = FastAPI()
FastAPIRedis(app).lifespan().caching()

```

## Caching data

At this point, you are ready to consume the caching functionality of the framework. You just simply use dependency injection and set up a caching rule:

```python
# cache(): read-path caching
@app.get(
    "/users/{user_id}", 
    dependencies=[
        Depends(cache(ttl=60, eviction_group="users"))
    ],
)
async def get_user(user_id: int) -> User:
  return await db.get_user(user_id)

# cache_evict(): invalidate the cached entry on delete
@app.delete(
    "/users/{user_id}",
    dependencies=[
        Depends(cache_evict(eviction_group="users", key_builder=usr_key_builder))
   ],
)
async def delete_user(user_id: int):
  await db.delete_user(user_id)

# cache_put(): write-through on update
@app.put(
    "/users/{user_id}",
    dependencies=[
        Depends(cache_put(eviction_group="users", ttl=300))
  ],
)
async def replace_user(user_id: int, body: User):
  return await db.update(user_id, body)

```

So now you have everything handled for you:

- The `GET` endpoint is cached, with a time-to-live of 60 seconds, so repeated requests for the same user identifier get fetched by the Redis cache, rather than hitting the database

- The `DELETE` endpoint automatically invalidates any cache entries for the provided ID

- The `PUT` endpoint could do a piece of magic—if the storing operation is successful (and only if it is successful)—we would actually update the cache with the new stored value, saving the first get call after that

The eviction group, on the other hand, could be used to identify data that should be evicted together, which would help improve consistency of the cache in case some of the cached entries only make sense together. It comes at a cost, however, as in order to make the eviction operation atomic the data is stored in the same slot in case of a cluster environment.

To read more on caching, [visit the caching section of our guide](https://redis.github.io/fastapi-redis-sdk/guide/caching/).

## Rate limiting

In a similar way, you need to configure the SDK to watch for rate limiting rules:

```python
FastAPIRedis(app).lifespan().rate_limiting()
# ... or in case you need both caching and rate limiting:
FastAPIRedis(app).lifespan().caching().rate_limiting()

```

Then it is as easy as:

```python
@app.get(
    "/api", 
    dependencies=[
        Depends(rate_limit("100/minute"))
    ]
)
async def api():
    return {"ok": True}

```

Or if you’d like to handle both burst and sustained rate limiting you can stack two separate rate limit declarations with different scopes to ensure they are counted separately and do not overwrite each other:

```python
@app.get(
    "/search",
    dependencies=[
        Depends(rate_limit("10/second", scope="search:burst")),       # burst
        Depends(rate_limit("100/minute", scope="search:sustained")),  # sustained
    ],
)
async def search():
    return {"results": [...]}

```

More on rate limiting can be found in the [rate limiting section](https://redis.github.io/fastapi-redis-sdk/guide/rate-limiting/) of the online guide.

## Configuration

The fastapi-redis-sdk follows the[ Pydantic Settings](https://pydantic.dev/docs/validation/latest/concepts/pydantic_settings/) configuration model, [as recommended by the FastAPI team](https://fastapi.tiangolo.com/advanced/settings), to ensure portability and type safety. Typically (on cloud environments such as FastAPI Cloud) the configuration parameters will be injected as environment variables; and as long as the names are correct they will be consumed automatically by the endpoints deployed.

So for cloud environments the configuration would look like:

```javascript
export REDIS_URL=redis://user:pass@host:6379/0

```

On development environments you can have separate `.env` files:

```
# .env-local
REDIS_URL=redis://localhost:6379/0

# .env-staging
REDIS_URL=redis://staging-env:6379/0

```

A complete list of configuration parameters can be seen at the [respective section of the guide](https://redis.github.io/fastapi-redis-sdk/guide/configuration/).

## Advanced recipes

When injecting the `CacheBackend` or `RateLimitBackend` in your endpoints (synchronous or asynchronous—it works for both) you are able to implement some advanced caching and rate limiting recipes, including but not limited to, the ones listed below.

## Caching

**Conditional caching****: **cache only when certain business rules are met

```python
@app.get("/items/{item_id}")
async def get_item(item_id: int, cache: CacheBackendDep):
    cached = await cache.get(f"item:{item_id}", eviction_group="items")
    if cached is not None:
        return cached

    item = await db.get_item(item_id)

    if item["status"] == "published":
        await cache.set(f"item:{item_id}", item, ttl=300, eviction_group="items")

    return item

```

**Cascade invalidations**: across eviction groups or when eviction groups could not be used

```python
@app.put("/profile/{user_id}")
async def update_profile(user_id: int, body: ProfileUpdate, cache: CacheBackendDep):
    await db.update_profile(user_id, body)

    # Cascade: profile, dashboard, and user list all become stale
    await cache.delete(f"profile:{user_id}", eviction_group="profiles")
    await cache.delete(f"orders:{user_id}", eviction_group="dashboard")
    await cache.delete("all", eviction_group="users")
    return {"ok": True}

```

**Dynamic TTL**: set the time-to-live based on the data itself

```python
@app.get("/content/{content_id}")
async def get_content(content_id: int, cache: CacheBackendDep):
    cached = await cache.get(f"content:{content_id}", eviction_group="content")
    if cached is not None:
        return cached

    content = await db.get_content(content_id)
    ttl = 3600 if content["premium"] else 300
    await cache.set(f"content:{content_id}", content, ttl=ttl, eviction_group="content")
    return content

```

Of course all of these patterns could be used together for advanced use cases. See the [section in the guide](https://redis.github.io/fastapi-redis-sdk/guide/caching/#2-cachebackend) that concerns these topics for more.

## Rate limiting

**Limiting a downstream resource**: when you want to protect a resource your endpoint depends on, and not the endpoint itself

```python
@app.post("/notify")
async def notify(to: str, limiter: RateLimitBackendDep):
    # 3 emails per recipient per hour, enforced across every worker
    if not (await limiter.hit(f"email:{to}", limit=3, window=3600)).allowed:
        raise HTTPException(429, "too many emails to this recipient")
    await send_email(to)
    return {"sent": True}

```

**Per-tenant fairness**: when you have a noise tenant in a multi-tenant environment

```python
@app.post("/reports")
async def build_report(body: ReportRequest, limiter: RateLimitBackendDep):
    if not (await limiter.hit(f"tenant:{body.tenant_id}", limit=20, window=60)).allowed:
        raise HTTPException(429, "tenant report quota exceeded")
    return await generate(body)

```

**Token budget**: compute the cost at runtime based on usage

```python
@app.post("/complete")
async def complete(body: CompletionBody, limiter: RateLimitBackendDep):
    cost = max(1, estimate_tokens(body.prompt))                # known only now
    result = await limiter.hit(
        f"tokens:{body.api_key}", limit=100_000, window=86_400, cost=cost,
    )
    if not result.allowed:
        raise HTTPException(429, f"daily budget exhausted; retry in {result.retry_after}s")
    return {"charged": cost, "remaining": result.remaining}

```

## Try it today

If you’d like to try the SDK, check out the [repository](https://github.com/redis/fastapi-redis-sdk), the [guide](https://redis.github.io/fastapi-redis-sdk/), and the [PyPI package](https://pypi.org/project/fastapi-redis-sdk/). We’d love for you to test it, share feedback, and suggest improvements or new features.