How do I get a list of column names from a psycopg2 cursor?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When you execute a query with psycopg2, the cursor stores metadata about the result set in cursor.description. That metadata includes column names, which makes it easy to build dictionaries, label exported rows, or inspect dynamic query results without hardcoding the schema in Python.
Read Column Names From cursor.description
After executing a query, each entry in cursor.description describes one output column. The first item in each entry is the column name.
That is the standard answer for query-result column names in psycopg2.
Remember That the Query Must Run First
cursor.description is populated by the last executed statement that produced a result set. If you inspect it before executing a SELECT, it will be None.
That detail matters when writing helper functions. The column metadata belongs to the current result set, not to the table in the abstract.
Use It to Build Row Dictionaries
A common use case is turning raw tuples into dictionaries keyed by column name.
This is handy when the query changes dynamically or when you want lightweight structured output without switching cursor factories.
Consider RealDictCursor for Named Rows
If your main goal is dictionary-like rows, psycopg2.extras.RealDictCursor can avoid the manual zip step.
Even when using RealDictCursor, understanding cursor.description is still useful because it explains where those column labels come from.
Query Results, Not Table Definitions
cursor.description reflects the columns of the executed query result, which means aliases and computed expressions appear exactly as selected.
This is often better than querying table metadata separately because it tells you what your actual SQL returned, not just what the underlying table schema contains.
Use It Carefully With Non-SELECT Statements
For statements that do not produce a result set, cursor.description stays None.
If your code handles arbitrary SQL, check for None before iterating.
Common Pitfalls
- Reading
cursor.descriptionbefore executing a query that returns rows. - Assuming it describes table metadata rather than the current query result.
- Forgetting that aliases appear exactly as written in the SQL.
- Treating non-SELECT statements as if they should have column metadata.
- Reimplementing row-dictionary behavior when a cursor factory might already solve the real need.
Summary
- In
psycopg2, column names come fromcursor.descriptionafter a result-producing query runs. - The first element of each description entry is the output column name.
- The metadata describes the query result, including aliases, not just the base table.
- Use it to build dictionaries or inspect dynamic queries.
- Check for
Nonewhen the statement may not return rows.

