---
cuppy_hub: aws
cuppy_path: reference/sns/filter-policies.md
cuppy_section: docs
cuppy_doc: WHD6-N7Q4
cuppy_doc_uuid: 8d986a87-80f8-4d80-8f32-2bf89b220b6e
cuppy_permalink: https://cuppy.io/aws/d/WHD6-N7Q4
cuppy_version: 4
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/aws/docs/reference/sns/filter-policies
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 6f4995730372d4e0f062b97421c419801d366c3fdff689a0ff4e87e226f26656
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# SNS subscription filter policies

**A filter policy sits on the *subscription*, not the topic, and every key in it must match.** Without a policy, the subscriber receives everything. Policies filter **message attributes** unless the subscription's `FilterPolicyScope` is `MessageBody`, in which case the published message must be well-formed JSON. Changes can take **up to 15 minutes** to take effect, so a test right after editing proves nothing.

## Syntax

A policy maps each property name to a **list** of allowed values or operators. Values in one list are alternatives (OR); different keys must all match (AND). Properties the policy doesn't mention are ignored.

| Want | Policy fragment |
|---|---|
| Exact (any of) | `"customer_interests": ["rugby", "tennis"]` |
| Anything but | `"customer_interests": [{"anything-but": ["rugby", "tennis"]}]` |
| Anything but a prefix / suffix / wildcard | `"event": [{"anything-but": {"prefix": "order-"}}]`, `[{"anything-but": {"suffix": "ball"}}]`, `[{"anything-but": {"wildcard": "*ball"}}]` |
| Case-insensitive | `"customer_interests": [{"equals-ignore-case": "tennis"}]` |
| Prefix / suffix | `[{"prefix": "bas"}]` / `[{"suffix": "ball"}]` |
| Wildcard | `"customer_interests": [{"wildcard": "*ball"}]` |
| IP range | `"source_ip": [{"cidr": "10.0.0.0/24"}]` |
| Number equals | `"price_usd": [{"numeric": ["=", 301.5]}]` |
| Number range | `"price_usd": [{"numeric": [">", 0, "<=", 150]}]` |
| Number anything-but | `"price": [{"anything-but": [100, 500]}]` |
| Key present / absent | `"store": [{"exists": true}]` / `"store": [{"exists": false}]` |

**Matching rules:**

- **Strings are case-sensitive.** Quote strings; don't quote numbers.
- **Numbers** range from −10⁹ to 10⁹ with five decimal places. Number attributes have type `Number` or `Number.Array`.
- **`String.Array` attributes:** each element is matched separately, so any matching element passes. `anything-but` passes an array only if **none** of its elements is listed.
- **`exists: true`** needs a non-null, non-empty value. **`exists: false`** matches only if the message has at least one attribute, so a message with **no attributes at all doesn't match**. `exists` works on leaf keys only.
- `anything-but` can be combined with `"exists": false`.
- OR across different keys (`$or`) has its own page, which wasn't collected. Check the SNS guide's "AND/OR logic" before using it.

## Message-body scope

```
aws sns set-subscription-attributes --subscription-arn <arn> \
  --attribute-name FilterPolicyScope --attribute-value MessageBody
aws sns set-subscription-attributes --subscription-arn <arn> \
  --attribute-name FilterPolicy --attribute-value '{"detail":{"status":["PAID"]}}'
```

With body scope, nest the policy to mirror the JSON structure; only leaf keys count toward the key limit.

## Limits

| Limit | Value |
|---|---|
| Keys per policy | 5 (leaf keys for nested policies) |
| Value combinations | 150: the product of the value counts per key, times the nesting level for nested keys |
| Policy size | 256 KB |
| Filter policies per topic / per account | Default quotas; the account one can be raised through Service Quotas. Read the live values rather than assuming them (`FilterPolicyLimitExceededException` when hit) |

## When filtering seems wrong

| Symptom | Check |
|---|---|
| Subscriber gets nothing | Scope: the policy targets attributes, but the publisher put the field in the body, or the other way round. Case differences. `exists: false` on a message with no attributes. Edited less than 15 minutes ago |
| Subscriber gets everything | No `FilterPolicy` on *this* subscription. Policies are per subscription |
| Raw-delivery queue loses messages that pass the filter | More than 10 attributes with raw delivery: discarded (`patterns/sns-to-sqs-fanout.md`) |

EventBridge has a similar but richer pattern language (`reference/eventbridge/event-patterns.md`).


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 131 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/aws/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
