How to Remove Multiple Keys from a Dictionary in Python

To remove multiple keys from a dictionary in Python, loop over the keys and call d.pop(key, None) for each one. The None default means missing keys are skipped instead of raising KeyError. To keep the original intact, build a new dict with a comprehension:

for key in ("ssn_last4", "card_token"):
    customer.pop(key, None)

The examples strip private fields from US customer and order records, and I ran them all with Python 3.12.5.

You’ll see the pop loop, why del can stop halfway, building a filtered copy, removing keys by value without a RuntimeError, cleaning nested data, and which method is quickest.

Remove multiple keys with pop() in a loop

pop(key, default) removes the key and returns its value, or returns the default if the key isn’t there. That makes it safe for keys that may or may not exist:

customer = {"id": 1042, "name": "Emily Carter", "email": "emily@example.com",
            "ssn_last4": "1234", "card_token": "tok_9f2", "city": "Austin"}

for key in ("ssn_last4", "card_token", "phone"):     # "phone" isn't there - no error
    customer.pop(key, None)
print(customer)

removed = {k: customer.pop(k) for k in ("email",) if k in customer}
print("removed:", removed, "| keys left:", list(customer))

Output:

{'id': 1042, 'name': 'Emily Carter', 'email': 'emily@example.com', 'city': 'Austin'}
removed: {'email': 'emily@example.com'} | keys left: ['id', 'name', 'city']
Command Prompt output of Python removing multiple keys from a customer dictionary with pop and a None default
The SSN and card token are gone, the missing phone key is ignored, and the removed email comes back in its own dictionary.

The second part keeps what you removed, which is useful for logging. For the single-key version of this, see removing a key without an error.

Why does del stop halfway through?

del d[key] raises KeyError on the first missing key, and the keys after it are never removed:

customer = {"id": 1042, "name": "Emily Carter", "ssn_last4": "1234", "city": "Austin"}

try:
    for key in ("ssn_last4", "phone", "city"):
        del customer[key]
except KeyError as err:
    print("KeyError:", err)
print(customer)          # ssn_last4 is gone, city is still here

Output:

KeyError: 'phone'
{'id': 1042, 'name': 'Emily Carter', 'city': 'Austin'}

Here city survives because the loop stopped at phone. Use pop(key, None), or check if key in d first. The error itself is explained in Python dictionary KeyError.

Remove keys by building a new dictionary

When you shouldn’t change the original, say it’s shared or cached, filter into a new dict. Keys views also support set operations:

order = {"id": 2041, "total": 129.99, "card_token": "tok_9f2", "internal_note": "VIP", "state": "TX"}
private = {"card_token", "internal_note"}

public = {k: v for k, v in order.items() if k not in private}
print(public)

keep = order.keys() - private                 # set difference on the keys view
print(sorted(keep))
print("original still has", len(order), "keys")

Output:

{'id': 2041, 'total': 129.99, 'state': 'TX'}
['id', 'state', 'total']
original still has 5 keys
Command Prompt output of Python building a dictionary without private keys using a comprehension and keys set difference
The public copy has id, total and state only, the set difference lists the same keys, and the original keeps all five.

Put the keys to drop in a set so each in check is fast. This copy approach is covered in more depth in copying a dictionary without some of its keys.

How do I remove keys while iterating over a dictionary?

You can’t change a dict’s size while looping over it. Python raises a RuntimeError. Loop over a copy of the keys instead:

prices = {"Mug": 12.0, "Filters": 0.0, "Beans": 18.5, "Kettle": 0.0}

try:
    for item in prices:
        if prices[item] == 0.0:
            del prices[item]
except RuntimeError as err:
    print("RuntimeError:", err)

prices = {"Mug": 12.0, "Filters": 0.0, "Beans": 18.5, "Kettle": 0.0}
for item in list(prices):               # iterate over a copy of the keys
    if prices[item] == 0.0:
        del prices[item]
print(prices)

Output:

RuntimeError: dictionary changed size during iteration
{'Mug': 12.0, 'Beans': 18.5}
Command Prompt output of Python RuntimeError dictionary changed size during iteration, then deleting keys safely from a list copy
The first loop fails with dictionary changed size during iteration; looping over list(prices) removes the two zero-price items.

list(prices) takes a snapshot of the keys, so deleting is safe. To drop keys whose values are empty, see removing empty keys from a dictionary.

Remove keys from a nested dictionary

API responses nest dicts inside lists inside dicts. A small recursive function removes the keys at every level and returns a clean copy:

import json

def drop_keys(data, keys):
    """Return a copy of data with the keys removed at every level."""
    if isinstance(data, dict):
        return {k: drop_keys(v, keys) for k, v in data.items() if k not in keys}
    if isinstance(data, list):
        return [drop_keys(item, keys) for item in data]
    return data

response = {"user": {"name": "James Wilson", "password_hash": "x91", "token": "abc"},
            "orders": [{"id": 1, "total": 54.5, "token": "t1"}, {"id": 2, "total": 230.25, "token": "t2"}],
            "token": "top"}
print(json.dumps(drop_keys(response, {"token", "password_hash"}), indent=2))

Output:

{
  "user": {
    "name": "James Wilson"
  },
  "orders": [
    {
      "id": 1,
      "total": 54.5
    },
    {
      "id": 2,
      "total": 230.25
    }
  ]
}
Command Prompt output of a Python recursive function removing token and password_hash keys from a nested JSON response
Every token and the password hash disappear from the user, from both orders and from the top level.

I use this before logging API data. For deeper KeyError problems in nested data, see KeyError in a nested Python dictionary.

Which way to remove multiple keys is fastest?

Here are 5,000 keys removed from a 10,000-key dict, best of three runs. The two in-place versions include the cost of copying the dict first:

import timeit

setup = "d0 = {f'k{i}': i for i in range(10_000)}; drop = {f'k{i}' for i in range(0, 10_000, 2)}"
tests = {
    "pop loop (in place)": "d = d0.copy()\nfor k in drop: d.pop(k, None)",
    "comprehension (new)": "d = {k: v for k, v in d0.items() if k not in drop}",
    "del with check": "d = d0.copy()\nfor k in drop:\n    if k in d: del d[k]",
}
for label, stmt in tests.items():
    t = min(timeit.repeat(stmt, setup, number=200, repeat=3))
    print(f"{label:<22} {t / 200 * 1000:.3f} ms per run (5,000 of 10,000 keys)")

Output:

pop loop (in place)    0.419 ms per run (5,000 of 10,000 keys)
comprehension (new)    0.474 ms per run (5,000 of 10,000 keys)
del with check         0.496 ms per run (5,000 of 10,000 keys)

The numbers are all small, so pick by intent: in place or a new dict. Counting what’s left is shown in counting the keys in a dictionary.

Ways to remove multiple keys compared

MethodChanges original?Missing key
for k in keys: d.pop(k, None)YesIgnored
for k in keys: del d[k]YesKeyError, stops
{k: v for k, v in d.items() if k not in keys}NoIgnored
d.keys() - keysNo (gives a set)Ignored
for k in list(d): ...YesRemoves by condition

A few more dictionary guides you might find useful:

Frequently asked questions

How do I remove multiple keys from a dictionary in Python?

Loop over the keys and call d.pop(key, None) on each, or build a new dict with a comprehension.

How do I remove keys without a KeyError?

Use pop(key, None). The default is returned when the key is missing.

Can I delete keys while looping over a dictionary?

Not directly: you get RuntimeError: dictionary changed size during iteration. Loop over list(d) instead.

How do I keep the original dictionary unchanged?

Use {k: v for k, v in d.items() if k not in drop}, which returns a new dict.

How do I remove a key from nested dictionaries?

Use a recursive function that rebuilds dicts and lists without the unwanted keys.

Where is dict.pop documented?

See dict.pop() in the Python docs.