Every Looker estate eventually meets the same request: "Can I get this in a spreadsheet?" or "Our data science team wants the certified revenue metric in a notebook, not a dashboard." The tempting answer is to hand out warehouse credentials and let people rebuild the logic in SQL. Six months later there are four definitions of active customer, none of which match the board deck.
Looker's Open SQL Interface exists to stop that. It exposes your LookML model as a JDBC-addressable, read-only SQL surface, so external tools query the semantic layer rather than the raw tables underneath. Joins, symmetric aggregates, access filters, and measure logic all still apply. This tutorial covers when to use it, how to switch it on, how to write queries against it, and where the sharp edges are.
What the Open SQL Interface actually is
The Open SQL Interface is an Avatica/Calcite-based JDBC endpoint served by your Looker instance. To the client tool it looks like a database:
- Each LookML model appears as a schema.
- Each Explore in that model appears as a table.
- Each dimension and measure appears as a column, named
view_name.field_name.
A query such as SELECT ... FROM order_items ... is not passed through to your warehouse verbatim. Looker parses it, maps the selected columns to LookML fields, and generates the same SQL an Explore would generate — including the join graph declared in the Explore, symmetric aggregates where a join fans out, and any access_filter or sql_always_where rules attached to the querying user.
That last point is the reason this feature matters more than a warehouse service account: row-level security travels with the query. The JDBC connection authenticates as a Looker user via API credentials, so a regional manager pulling data into a notebook sees the same rows they would see in an Explore.
It is read-only. There is no INSERT, no CREATE TABLE, no DDL. It is a distribution channel for governed metrics, not a database.
When to reach for it (and when not to)
Use it when:
- A data science or engineering team wants governed metrics in Python, R, or a notebook.
- Another BI or reporting tool in the organisation needs to consume Looker-defined metrics rather than reinventing them.
- A spreadsheet-heavy finance team wants refreshable extracts that respect the model.
- You are consolidating "shadow SQL" — the pile of hand-written queries that quietly disagree with Looker.
Do not reach for it when:
- You need high-concurrency, sub-second serving. Every query still lands on your warehouse; the interface adds Looker's parsing and queueing on top.
- You want a bulk export of a billion rows. Use a warehouse export or a scheduled delivery to cloud storage instead.
- The consumer is an application UI. That is what embedding and the Looker SDK are for.
- You need writeback. It is read-only by design.
Step 1: Enable the interface
Two switches, both admin-level:
- Admin > Platform > SQL Interface — enable the feature for the instance. Depending on your Looker version and edition this may appear as a labs/legacy feature toggle; check that the instance is on a current release first.
- Per connection — open the database connection in Admin > Database > Connections and confirm the SQL Interface option is enabled there too. Connections without it will not expose their models.
Then confirm the JDBC endpoint. It follows the pattern:
jdbc:looker:url=https://yourcompany.cloud.looker.com:443
For Looker (Google Cloud core) instances the host is your instance URL; port 443 unless your instance publishes an alternate API port.
Step 2: Get the driver and credentials
Download the Looker JDBC driver (looker-jdbc.jar) from the Admin > Platform > SQL Interface page — Looker serves the version matched to your instance, which matters, because driver/instance version skew is the single most common cause of "connects but returns nothing".
Credentials are API3 credentials, not a password:
- Username = API client ID
- Password = API client secret
Generate them per user in Admin > Users > Edit > API Keys. For a shared integration, create a dedicated service user with a role scoped to exactly the model(s) you want to expose, plus any user attributes needed for access filters. Do not point production integrations at a departing analyst's personal key.
A minimal Java/JDBC connection string:
jdbc:looker:url=https://yourcompany.cloud.looker.com:443;
user=<client_id>;password=<client_secret>
From Python, the practical route is JayDeBeApi or a JDBC bridge in your orchestration tool:
import jaydebeapi
conn = jaydebeapi.connect(
"com.looker.jdbc.Driver",
"jdbc:looker:url=https://yourcompany.cloud.looker.com:443",
{"user": CLIENT_ID, "password": CLIENT_SECRET},
"/opt/drivers/looker-jdbc.jar",
)
cur = conn.cursor()
cur.execute("""
SELECT
`users.country` AS country,
`order_items.total_sale_price` AS revenue
FROM order_items
WHERE `order_items.created_date` >= DATE '2026-01-01'
GROUP BY 1
ORDER BY 2 DESC
LIMIT 25
""")
for row in cur.fetchall():
print(row)
Step 3: Learn the query dialect
This is where teams stumble. The interface speaks Calcite SQL, and the objects are LookML, so a few rules apply that would not apply to your warehouse:
Field names are view.field, and they need quoting. Backticks or double quotes depending on the client. SELECT users.country FROM order_items will be misread as a table reference; SELECT `users.country` is correct.
The FROM clause takes an Explore, not a view. FROM order_items means "the order_items Explore in the connected model", not the underlying table. Joins are already defined; you generally do not write JOIN at all. If a field you want is not reachable from that Explore's join graph, the query fails — the fix is a LookML change, not a SQL change.
Measures aggregate themselves. `order_items.total_sale_price` is already a type: sum measure. Do not wrap it in SUM(). Do include a GROUP BY for the dimensions you select; Looker maps that onto the Explore's grouping.
Filters map to Explore filters. Simple predicates on dimensions translate cleanly. Exotic expressions, window functions over the result, and correlated subqueries mostly do not — push that logic into LookML as a measure or a derived table instead.
LIMIT matters. Row limits from the instance still apply, and pulling wide unaggregated result sets through JDBC is slow. Aggregate on the Looker side.
A good mental model: if you could build the query in an Explore, it will work over JDBC. If you could not, it probably will not.
Step 4: Connect a client tool
Anything that accepts a JDBC driver works. Common patterns we set up for clients:
- Notebooks / Python pipelines — JayDeBeApi as above, or a Spark JDBC read for larger pulls.
- DBeaver / DataGrip — register
looker-jdbc.jaras a custom driver with classcom.looker.jdbc.Driver, then browse models as schemas. This is the fastest way to sanity-check the interface after enabling it. - Third-party BI tools — point them at the Explore rather than raw tables. You lose Looker's visualisation layer but keep one definition of every metric.
- Google Sheets — for spreadsheet users, Looker's own scheduled deliveries or Connected Sheets against BigQuery are usually a better fit than a JDBC bridge; reserve the SQL Interface for programmatic consumers.
Governance: what to lock down before you announce it
Opening a query surface changes your risk profile. Before you tell the wider business it exists:
Scope the service user's role. Model-level permissions decide which schemas are visible. A JDBC service user should see the certified models and nothing else.
Confirm access filters fire. Run the same query as a restricted user and as an admin and diff the row counts. If they match, your access_filter is not wired to the user attribute you think it is. (Our post on row-level security patterns covers the usual failure modes.)
Watch the warehouse bill. Every JDBC query is a warehouse query. Tag it: System Activity records SQL Interface queries alongside Explore queries, so you can see who is pulling what and how often. Aggregate awareness helps here too — a JDBC query that hits an aggregate_table costs the same as a dashboard tile that hits it.
Set expectations on latency. Looker's query queue, caching policy, and datagroups apply. A cold query on a large Explore is not going to feel like a local database, and users coming from raw warehouse access will notice.
Version the contract. Consumers now depend on field names. Renaming a dimension in LookML silently breaks someone's notebook. Treat exposed Explores as a public API: rename via alias, deprecate before deleting, and note exposed Explores in your CI checks.
A worked example: certifying one metric
Say finance maintains a spreadsheet definition of net revenue that disagrees with the dashboard. The migration looks like this:
- Model it once. Define
net_revenueas a measure in LookML, with the discount and refund logic insql:rather than in the spreadsheet. - Expose it in a dedicated Explore. A narrow
finance_reportingExplore with the dimensions finance actually uses is easier to support than opening your kitchen-sink Explore. - Create a
svc_finance_jdbcservice user, role limited to that model, with a user attribute for entity access. - Hand finance a parameterised query, not credentials-plus-a-shrug. One query, checked into their repo, that they can re-run.
- Retire the spreadsheet formula. This is the step people skip, and it is the only step that actually removes the second definition.
The point is not the JDBC connection. It is that the number in the notebook and the number on the dashboard are now generated by the same LookML.
Common errors and what they mean
| Symptom | Usual cause |
|---|---|
Driver not found | Wrong class name, or driver jar not on the classpath. Class is com.looker.jdbc.Driver. |
| Connects, no schemas listed | SQL Interface not enabled on the connection, or the user's role has no model access. |
Unknown field on a valid-looking column | Missing view. prefix, or the field is not joined into that Explore. |
| Numbers double-counted | You wrapped a measure in SUM(), or you are reading a fanned-out join without the measure. |
| Query hangs then times out | Unaggregated wide select. Add a GROUP BY and a LIMIT, or point at an aggregate table. |
| Worked last month, fails now | Driver/instance version skew after an instance upgrade. Re-download the jar. |
Where this fits
The Open SQL Interface is the least glamorous of Looker's distribution channels — no dashboards, no AI, just a JDBC string — and it is frequently the highest-leverage one. Embedding serves applications. The API 4.0 SDK serves automation. Conversational Analytics serves natural-language questions. The SQL Interface serves the analysts and engineers who were going to write SQL anyway, and it lets them write it against your governed model instead of around it.
If you are weighing whether to open this up, or you have opened it and your warehouse bill has opinions about that, Vistelio's senior Looker developers do exactly this kind of work: modelling the certified metrics, scoping the service roles, and putting the exposed Explores under CI so a rename never breaks a downstream consumer. Get in touch and tell us what your consumers are asking for.