Quick answer
You indexed a string with a text key (data['name']) — but strings only accept integer positions. The value is a string where you expected a dict. Usually you forgot to parse JSON (json.loads(text) first), or you are looping over a dict, which yields its keys (strings).
Diagnose this error in under a minute
Before changing anything, print the value's type — it points straight to the fix you need:
print(type(data))
print(repr(data)[:200])
<class 'str'>→ it's JSON text — parse it first withjson.loads()/response.json()(Cause 1)<class 'dict'>→ you're looping over it (keys are strings) or using a wrong key — iterate.values()/.items()(Cause 2)<class 'list'>→ index by number first, then by key:data[0]['key'](Cause 3)- still a
strafter onejson.loads→ the payload was double-encoded — parse again or fix the producer (Cause 4)
The exact error string
data = '{"name": "Ada", "age": 36}' # this is a STRING, not a dict
print(data['name'])
Traceback (most recent call last):
File "app.py", line 2, in <module>
print(data['name'])
TypeError: string indices must be integers, not 'str'
Python read data['name'] as "give me index 'name' of this string," and string indexes must be integers like data[0]. The not 'str' suffix (Python 3.11+) confirms you handed it a string index.
Where you'll usually see this
It almost always appears at the boundary where text becomes data — the same root cause across the Python web stack:
- requests — using
resp.textinstead ofresp.json() - Flask / FastAPI / Django — reading a raw request body without parsing it
- AWS Lambda —
event['body']arrives as a JSON string; parse it before indexing - config & JSON files —
f.read()returns text; usejson.load(f)instead
Cause 1: you forgot to parse the JSON
A response body, a file's contents, or a message payload is text. Until you parse it, indexing by key fails:
import json, requests
resp = requests.get("https://api.example.com/user")
# ❌ resp.text is a JSON string
data = resp.text
data["name"] # TypeError: string indices must be integers
# ✅ parse it into a dict first
data = resp.json() # or json.loads(resp.text)
data["name"] # 'Ada'
The same applies to files: use json.load(f) (parses) rather than f.read() (returns text). If you are not sure what the payload looks like, paste it into the JSON Formatter to see whether it is an object you can key into.
Cause 2: looping over a dict gives you keys
Iterating a dict yields its keys — which are strings — so indexing the loop variable fails:
users = {"u1": {"name": "Ada"}, "u2": {"name": "Bo"}}
# ❌ item is the key string "u1", so item["name"] fails
for item in users:
print(item["name"]) # TypeError: string indices must be integers
# ✅ iterate values (or items) to get the inner dicts
for user in users.values():
print(user["name"]) # 'Ada', 'Bo'
for key, user in users.items():
print(key, user["name"])
Cause 3: a list of dicts indexed wrong
If the data is a list of objects, you must index the list by number first, then the object by key — not the other way round:
rows = [{"name": "Ada"}, {"name": "Bo"}]
rows["name"] # ❌ rows is a list — wrong
rows[0]["name"] # ✅ 'Ada'
for row in rows: # ✅ each row is a dict
print(row["name"])
Cause 4: double-encoded JSON
If one json.loads still leaves you with a string, the payload was encoded twice — a value that is itself a JSON string:
raw = '"{\\"name\\": \\"Ada\\"}"' # a JSON string whose content is JSON
once = json.loads(raw) # -> '{"name": "Ada"}' (still a str!)
once["name"] # TypeError again
twice = json.loads(once) # -> {'name': 'Ada'}
twice["name"] # 'Ada' — but fix the producer if you can
Errors often confused with this one
These Python errors come from the same family of "wrong type / wrong shape" mix-ups:
- TypeError: the JSON object must be str, bytes or bytearray, not dict — the inverse: parsing something that's already parsed
- KeyError — the value is a dict, but the key is missing
- JSONDecodeError — the text isn't valid JSON to begin with
AttributeError: 'str' object has no attribute ...andIndexError— the same "I assumed the wrong type" confusion, on a string or a list
Debugging checklist
- ✓ Print
type(data)— is itstrordict? - ✓
strthat looks like JSON? Runjson.loads()/response.json()first - ✓ In a
forloop over a dict? Iterate.values()or.items() - ✓ Is it a list of dicts? Index by number first:
data[0]['key'] - ✓ Still a string after one parse? It was double-encoded — parse again or fix the source
- ✓ Inspect the real shape in the JSON Formatter
Python versions
Applies to Python 3.8, 3.9, 3.10, 3.11, 3.12, and 3.13. The wording gained the , not 'str' suffix in 3.11+; the cause and every fix above are identical across all versions.
Frequently Asked Questions
What does 'string indices must be integers' mean?
It means you used a string as the index into another string — like text['name'] — but strings can only be indexed by integer positions (text[0]). Python expected a number and got text. It almost always means the value you indexed is a string when you thought it was a dictionary.
Why does it happen with JSON?
The most common cause is forgetting to parse JSON. A response body or file is text, so data['key'] fails because data is still a JSON string. Call json.loads(text) (or response.json()) first to turn it into a dict, then index it by key.
Why do I get it inside a for loop?
Iterating a dict yields its keys, which are strings. So for item in my_dict: makes item a key string, and item['field'] then fails. Loop over my_dict.values() or my_dict.items() instead, or index the dict directly with the key.
What is the Python 3.11 version of this message?
Python 3.11+ gives a more specific message: string indices must be integers, not 'str'. It is the same error with the offending index type named, which confirms you indexed a string with another string.
How do I confirm the variable is a string, not a dict?
Print its type and a slice: print(type(data)) and print(repr(data)[:200]). If it shows <class 'str'> and starts with a quote or a brace inside quotes, it is JSON text that still needs json.loads. If it is <class 'dict'>, the problem is a wrong key or a loop over keys.
Could double-encoded JSON cause it?
Yes. If an API returns JSON whose value is itself a JSON string, one json.loads gives you a string, not a dict, so indexing it fails. Parse again (json.loads twice) or, better, fix the producer so it does not double-encode the payload.
Not sure what shape your data is?
Paste the response into the formatter to see whether it's an object, an array, or a string — nothing is uploaded to a server.