The Python SDK lets you execute SurrealQL statements directly against the database. You can run ad-hoc queries with parameter binding, retrieve processed results, or access the full raw response for advanced use cases.
This page covers how to run queries, bind variables, and work with raw results.
API references
Method | Description |
|---|---|
db.query(query, vars?) | Builds a SurrealQL query; trigger it with .execute(), .first()or .into(cls) |
db.query_raw(query, vars?) | Executes a SurrealQL query and returns the full raw response |
Running a query
The .query() method builds a SurrealQL query. It does not talk to the database on its own — you trigger it explicitly:
.execute()returns alistof Value with one entry per statement, always a list, even when the query contains a single statement..first()returns just the first statement's result, which is what you usually want for a one-statement query.
On an async connection you can also await the builder directly, which is the same as awaiting .execute().
from surrealdb import Surreal
with Surreal("ws://localhost:8000") as db:
db.use("surrealdb", "docs")
db.signin({"username": "root", "password": "root"})
statements = db.query("SELECT * FROM users").execute()
print(statements) # [[{...}, {...}]] - one entry, one statement
users = db.query("SELECT * FROM users").first()
print(users) # [{...}, {...}]Passing variables
You can pass a dictionary of variables as the second argument to .query(). Variables are referenced in SurrealQL using the $ prefix and are safely bound, preventing injection attacks.
users = db.query(
"SELECT * FROM users WHERE age > $min_age AND active = $active",
{"min_age": 18, "active": True},
).first()You can bind any Python value supported by the SDK, including strings, numbers, booleans, lists, dictionaries, and SurrealDB-specific types such as RecordID.
from surrealdb import RecordID
users = db.query(
"SELECT * FROM users WHERE id = $user_id",
{"user_id": RecordID("users", "tobie")},
).first()Getting raw query results
The .query_raw() method returns the full response from the server, including metadata such as execution time and status for each statement. This is useful for debugging or when you need to inspect how the server processed the query.
response = db.query_raw("SELECT * FROM users; SELECT * FROM products")
for statement in response["result"]:
print(statement["status"])
print(statement["time"])
print(statement["result"])The response is a dict containing the RPC envelope. Its result key holds one entry per statement in the query, each with status, time, and result fields. Unlike .query(), a failed statement is reported as "status": "ERR" rather than raised.
Handling multiple statements
When a query string contains multiple semicolon-separated statements, .execute() returns one entry per statement, in order. Nothing is discarded, so you can unpack the results directly.
created, users = db.query("""
CREATE users CONTENT {"name": "Alice", "age": 30};
SELECT * FROM users;
""").execute()
# .first() would return only the CREATE result.query_raw() returns those same per-statement results wrapped in the raw RPC envelope, with each statement's status and time alongside its result. Reach for it when you need that metadata, or when you want failed statements reported rather than raised.
Learn more
Surreal API reference for complete method signatures and parameters
Data manipulation for CRUD operations using dedicated methods
SurrealQL reference for the full query language documentation
Value types for the types returned by query methods