PyPython example

How to check if a string contains a substring in Python

4 min read▶ Runs in an isolated hosted runtimeUpdated Jul 2026

Quick answer

Python has no .contains() method — the answer is the in operator: if "code" in text:. It returns True or False and is the fastest thing to read and to type.

in answers whether a substring is there, never where. When you need the position, reach for .find() or .index(); when case shouldn't matter, fold both sides first; when you're matching a shape rather than fixed text, use re.search(). Every example below runs on this page — hit Run, edit, run again.

1The in operatorRecommended

substring in string is a plain boolean test — no imports, no return code to interpret. It reads like English, so use it directly in an if rather than comparing something to -1.

contains.py

Output

Output: found it, True, False, False. That last line is the gotcha — in is case-sensitive, so "Code" misses the lowercase code in the text. Section 3 fixes that. Use not in for the negative check.

2find() and index() — when you need the position

Both return the index of the first occurrence. They differ only in how they report a miss: .find() returns -1, while .index() raises ValueError. Pick .find() when "not there" is a normal outcome and .index() when it means your data is wrong and you want the crash.

position.py

Output

Output: 23, -1, 23, then index raised ValueError: substring not found. Never write if text.find("x"): — a match at position 0 is falsy, so that silently misses hits at the start. Compare against -1, or just use in. Both methods also take start and end offsets, and .rfind() / .rindex() search from the right.

3Case-insensitive matching

There is no ignorecase flag on in — you normalise both sides yourself. .lower() is enough for ASCII, but .casefold() is the more correct choice: it is an aggressive, matching-oriented fold that handles cases .lower() leaves alone, such as German ßss.

case_insensitive.py

Output

Output: False, True, False, True. The third line is .lower() failing for real: "Große".lower() is still große, so grosse isn't in it — .casefold() turns it into grosse and the match succeeds. Fold once outside the loop if you're testing many needles against the same text.

4Patterns with re.search(), edges with startswith()

in matches literal text — "c.de" in text is False even though the regex c.de matches code. When you want a shape (a date, a number, an id), use re.search(), which returns a match object or None. When you only care about the start or the end, the dedicated .startswith() / .endswith() methods say so more precisely — and both accept a tuple of candidates.

patterns.py

Output

Output: True, 91%, True, True. Use re.search(), not re.match()match() only anchors at the start. And if your needle comes from user input, wrap it in re.escape() or characters like . and * will be read as pattern syntax.

5Which should you use?

MethodReturnsIf missingBest for
"x" in textTrue / FalseFalsePresence — the default
text.find("x")Index-1Position, miss is expected
text.index("x")IndexValueErrorPosition, miss is a bug
re.search(p, text)Match or NoneNonePatterns, not fixed text
text.startswith("x")True / FalseFalsePrefix / suffix only

6Common variation: check many substrings at once

Don't chain or across a dozen checks. Put the needles in a list and feed a generator to any() — it short-circuits on the first hit — or to all() when every term must appear.

many.py

Output

Output: True, False, ['timeout'], timeout. The comprehension gives you every match and next(..., None) gives you the first one without an IndexError on an empty result.

Frequently asked questions

Does Python have a string.contains() method?

No. Calling text.contains("x") raises AttributeError: 'str' object has no attribute 'contains'. The in operator is Python’s equivalent: "x" in text. The confusion usually comes from pandas, where a Series does have .str.contains().

What is the difference between find() and index()?

Both return the index of the first occurrence of the substring. When it is missing, find() returns -1 and index() raises ValueError: substring not found. Use find() when a miss is a normal outcome, index() when a miss means the data is wrong.

How do I check if a string contains a substring, ignoring case?

Fold both sides: needle.lower() in text.lower(). Prefer .casefold() for non-English text — it folds forms .lower() misses, so "GROSSE".casefold() in "Große Straße".casefold() is True while the .lower() version is False.

Why does if text.find("x") not work?

find() returns 0 when the substring is at the very start, and 0 is falsy — so the if skips a real match. Write if text.find("x") != -1: or, better, if "x" in text:.