Audit my site
Search ConsoleAdvancedChecked against Google docs · Oct 2026

Getting Search Console data out: API, BigQuery and Looker Studio

The Performance report shows 1,000 rows and keeps a limited history. The API, the Looker Studio connector and the bulk export to BigQuery each move more of the data out, with different limits, costs and skills. Here is what each one really delivers and when to use it.

Author: Eugene Serbin, Head of SEO at Semalt Updated: 30 October 2026 Reading time: 23 min Visuals: 4

In short

Search Console data leaves the interface four ways: the export button (1,000 rows), the Search Console API (up to 25,000 rows per request and about 50,000 rows per day per search type), the Looker Studio connector for dashboards, and the bulk data export to BigQuery, which delivers everything except anonymized queries every day. The first three are free and capped; the bulk export is uncapped but runs on your own Google Cloud billing and only starts on the day you set it up. Pick by row count, history and how much engineering you can support.

Four ways to get the data out

The Performance report is a view on top of one dataset. When you need more rows than the table shows, a history you control, a scheduled job or a dashboard for people who never open Search Console, you need to move that data somewhere else. Google offers four routes, and they are not interchangeable.

This lesson builds on the Performance report lesson: the metrics, filters and the difference between property and page aggregation are explained there. Here the focus is on getting the data out correctly.

Diagram · How Search Console data flows out
Search Console data flow Search Console feeds four routes: UI export to spreadsheets, the Search Analytics API to scripts and your own database, the bulk data export to a BigQuery dataset, and the Looker Studio connector directly to dashboards. Spreadsheets, your database and BigQuery can also feed dashboards. Search Console performance data UI exportSearch Analytics API Bulk data exportLooker Studio connector manual, 1,000 rowscode, ~50,000 rows/day/type daily, no row limitno code, API-level limits Spreadsheets Scripts, your DB BigQuery dataset your billing Dashboards Looker Studio
Every route starts from the same data; what changes is how many rows you get, how fresh they are and who pays for storage and queries.

The table below compares the four routes side by side. Figures come from Google's documentation; the history window is the standard Performance report window and is approximate.

Table · The four export options compared
UI exportSearch Analytics APILooker Studio connectorBulk export to BigQuery
Rows1,000 per tableUp to 25,000 per request; about 50,000 per day per site per search typeSame ceiling as the APINo daily row limit
HistoryWhat the report shows (about 16 months)Same windowSame windowFrom setup day onwards, kept until you expire it
FreshnessComplete days; preliminary only if you pick themFinal data by default; fresh data with dataStateFollows the connectorOnce a day; first export up to 48 hours after setup
Anonymized queriesOmitted from the tableNo row for themMay appear as one blank rowFlagged, query is an empty string
AutomationNoneFullReport refreshAutomatic daily load
CostFreeFree within quotaFreeBigQuery storage and queries on your billing (free usage level)
SkillsNoneCode, OAuthDrag and dropSQL, Google Cloud admin
Best forOne-off checksScheduled pulls, archives, toolsStakeholder dashboardsLarge sites, long tail, joins
The bulk export is the only route without a daily row ceiling; it is also the only one that costs money and cannot look back before its start date.

What Google officially says

“The API is bounded by internal limitations of Search Console and does not guarantee to return all data rows but rather top ones.”

Search Console API — Search Analytics: query

“The maximum you can export through the Search Console user interface is 1,000 rows of data.”

Google Search Central Blog — A deep dive into Search Console performance data filtering and limits. For the API and the Looker Studio connector the limit “is 50,000 rows per day per site per search type, which may not be reached in all cases.”

“Using the bulk data export feature, you'll see all the performance data available to Search Console for your property, with the exception of anonymized queries.”

Search Console Help — About bulk data export of Search Console data to BigQuery

“Data is subject to Google Cloud storage and query costs, but there is a free usage level.”

Search Console Help — Start a new bulk data export

Google also states who the bulk export is for: “Small and medium sites already have access to all their data through the user interface, the Looker Studio connector (formerly known as Data Studio) or the Search Analytics API.” If your site is not large, the free routes are usually enough.

UI exports and their limits

Most Search Console reports have an export button that downloads the chart and table data as Google Sheets, Excel or CSV. For the Performance report this is the fastest way to get numbers into a spreadsheet, and it has three properties you must know before you trust the file.

Two details make the cap less painful. First, it applies per export, so a filter (one folder, one country, one device) gives you a different set of 1,000 rows. Second, Google notes that “For requests that don't involve query or URL dimensions, such as countries, devices, and Search Appearances, Search Console will display and export all the data.” The cap only bites on queries and pages.

The UI export is enough for a one-off look at the top queries or pages of a small site. It is the wrong tool for tracking change over time, for long-tail analysis or for anything you need to repeat every week.

The Search Console API: what you can query

The Search Console API has several resources (sites, sitemaps, URL Inspection), but for performance data you use one method. In Google's words, it lets you “Query your search traffic data with filters and parameters that you define.” It returns rows grouped by the dimensions you choose, with clicks, impressions, CTR and position for each row.

Endpoint, property and scope

POST https://www.googleapis.com/webmasters/v3/sites/{siteUrl}/searchAnalytics/query

The siteUrl must match the property exactly as it exists in Search Console, URL-encoded: Google's examples are “http://www.example.com/ (for a URL-prefix property) or sc-domain:example.com (for a Domain property)”. A Domain property therefore becomes sc-domain%3Aexample.com in the path. Requests need OAuth with one of two scopes: https://www.googleapis.com/auth/webmasters.readonly or https://www.googleapis.com/auth/webmasters. For reporting, always use the read-only one.

Request body fields

FieldWhat it does
startDate, endDateRequired, YYYY-MM-DD, both inclusive, in Pacific Time. “You must define a date range of one or more days.”
dimensionsZero or more of query, page, country, device, searchAppearance, date, hour. No dimensions returns one total row.
typeweb (default), image, video, news, discover, googleNews. web is the combined All tab and does not include Discover or Google News. The old searchType field is deprecated.
dimensionFilterGroupsFilters with operators equals (default), notEquals, contains, notContains, includingRegex, excludingRegex. Regex uses RE2 syntax. Countries are 3-letter codes such as usa; devices are DESKTOP, MOBILE, TABLET.
aggregationTypeauto (default), byPage, byProperty, byNewsShowcasePanel. “Note: If you group or filter by page, you cannot aggregate by property.”
rowLimit“Valid range is 1–25,000; Default is 1,000”.
startRowZero-based offset for pagination.
dataStatefinal (default), all to include fresh data, hourly_all for hourly breakdown when grouping by hour. “If "final" (case-insensitive) or if this parameter is omitted, the returned data will include only finalized data.”

An example request

Top queries and pages for the blog folder on mobile, for one day, from a Domain property:

POST https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query
Authorization: Bearer ACCESS_TOKEN

{
  "startDate": "2026-10-26",
  "endDate": "2026-10-26",
  "type": "web",
  "dimensions": ["query", "page"],
  "dimensionFilterGroups": [{
    "groupType": "and",
    "filters": [
      { "dimension": "page", "operator": "includingRegex", "expression": "^https://example\\.com/blog/" },
      { "dimension": "device", "operator": "equals", "expression": "MOBILE" }
    ]
  }],
  "rowLimit": 25000,
  "startRow": 0
}

The response is a list of rows. Each row has keys in the order of your dimensions, then clicks, impressions, ctr (from 0 to 1, not a percentage) and position, plus responseAggregationType for the whole response. The numbers below are illustrative:

{
  "rows": [
    { "keys": ["how to clean suede shoes", "https://example.com/blog/suede-care/"],
      "clicks": 41, "impressions": 980, "ctr": 0.0418, "position": 3.2 }
  ],
  "responseAggregationType": "byPage"
}

Three behaviours to plan for. “Results are sorted by click count descending”, except when you group by date, which sorts oldest first. “When date is one of the dimensions, any days without data are omitted from the result list”, so a missing day is a gap, not a zero row. And search appearance works in two steps: “Search appearance is not available as a column along with any other dimensions.” First query with searchAppearance as the only dimension to list the types, then filter by one type and add other dimensions.

API limits: rows, pagination, quotas and data delay

Rows and pagination

One request returns at most 25,000 rows. To get more, repeat the same request and move the offset: “Page through results by re-running the same query, increasing the startRow value by 25,000 in the request until you reach the last page (a response with 0 rows).” An empty page is the normal end of the loop, not an error: “If startRow exceeds the number of results for the query, the response will be a successful response with zero rows.”

Pagination does not remove the ceiling. “In addition to API usage quota, the Search Analytics method exposes a maximum of 50K rows of data per day per search type (web, image, and so on--sorted by clicks).” Detail also costs completeness: “When you group by page and/or query, our system may drop some data in order to be able to calculate results in a reasonable time using a reasonable amount of computing resources.” For exact totals, run a second query without page and query, grouped only by date, country or device.

Quotas

Search Analytics has two kinds of quota. Load quota measures how expensive your queries are, in 10-minute and 1-day windows. “Most users will not exceed load limits”; if you do, the API returns a quota exceeded error, the same error for every kind of quota. Google's fixes:

Request quota limits how many calls you make. The numbers are in the cheat sheet below, together with every other documented limit.

Cheat sheet · Every documented number
1,000 rows

UI table and UI export. Also the API default rowLimit.

1–25,000

Valid rowLimit per API request. Page with startRow in steps of 25,000.

50,000 rows

API and connector ceiling per day, per site, per search type, sorted by clicks.

1,200 QPM

Search Analytics, per site and per user.

40,000 QPM · 30,000,000 QPD

Search Analytics, per Cloud project.

600 QPM · 2,000 QPD

URL Inspection, per site.

15,000 QPM · 10,000,000 QPD

URL Inspection, per Cloud project.

20 QPS · 200 QPM

All other API resources, per user; 100,000,000 QPD per project.

15 minutes

Wait after a short-term load quota error, then retry.

2–3 days

Typical delay before data is available.

48 hours

Latest time for the first bulk export after setup.

14 days

Minimum partition expiration for bulk export tables.

about a week

How long the bulk export retries a missed day.

about 16 months

History in the Performance report and the API (standard window, not a quota).

Quotas are from the Search Console API usage limits page; you can see your project's usage in the quota tab of the Google Cloud console.

Data delay and the daily pull

“Data is typically available after 2-3 days”, and the help center says the same: “Collected data is usually available in 2–3 days.” If you need fresher numbers, set dataState to all; the response then includes recent, still-changing rows, and the metadata object tells you where incomplete data starts (first_incomplete_date, or first_incomplete_hour with hourly_all).

Google's recommended pattern for complete data is simple: run a query each day for a single day, about three days back, for each search type you care about, and page through until you get zero rows. One day per request keeps load low and stays inside the 50,000-row ceiling per day rather than per range.

Bulk data export to BigQuery

The bulk export removes the row ceiling. “You can configure an export in Search Console to get a daily data dump into your BigQuery project.” The data then lives in your Google Cloud project, where you can query it with SQL, join it with other data and keep it as long as you like.

Setup

  1. Prepare a Google Cloud project with billing. Enable the BigQuery API and the BigQuery Storage API.
  2. Grant access to Google's export account. In IAM, add the principal [email protected] with two roles: BigQuery Job User and BigQuery Data Editor. You do not create this account; it belongs to Search Console.
  3. Configure the export in Search Console. Settings > Bulk data export, paste the project ID (not the project number), choose a dataset name (it always starts with searchconsole; use a different one per property if several properties share a project) and a dataset location, which you cannot easily change later.
  4. Wait. “The first export will happen up to 48 hours after your successful configuration in Search Console.”

Only owners can do step 3: “Note that only property owners can set up a bulk data export.” And there is no backfill: “If you want to see historical data that precedes your initial setup, use the Search Console API or the reports.” Set up the export early, and pull the older months through the API once if you need them.

The three tables

TableContainsKey fields
searchdata_site_impressionData aggregated by property, like the Performance chart and the Queries tabdata_date, site_url, query, is_anonymized_query, country, search_type, device, impressions, clicks, sum_top_position
searchdata_url_impressionData aggregated by URL, like the Pages tab, with search appearance flagsThe same plus url, is_anonymized_discover, is_[search_appearance_type] booleans and sum_position
ExportLogOne row per successful export of each table and dayagenda, namespace, data_date, epoch_version, publish_time

All dates are Pacific Time and the data tables are partitioned by data_date. “Search Console exports bulk data once per day, though not necessarily at the same time for each table.” If a day fails for a reason you must fix, such as lost permissions, Search Console retries that day for about a week and then gives up on it. When Google corrects old data, the whole day is replaced and epoch_version goes up by one.

How anonymized data looks

The export is complete except for privacy filtering. Anonymized queries are, in Google's definition, “Anonymized queries are those that aren't issued by more than a few dozen users over a two-to-three month period.” Their traffic is still in the tables, but without the query text: “When is_anonymized_query is true, this will be a zero-length string.” For Discover, rows below the privacy threshold carry is_anonymized_discover, described as “Whether the data row is under the Discover anonymization threshold.” Such rows lack fields like URL and country.

This is a real difference from the API, which simply has no row for anonymized queries. In BigQuery you can measure exactly how much of your traffic is anonymized, which the other routes cannot show you per day and URL.

Cost and retention

BigQuery bills the Google Cloud project, which means you, not Google, pay. “Remember that when you run a query on BigQuery you will be charged for it, and your tables can become quite large.” Storage grows every day: “Data will be accumulated forever for your project, unless you set an expiration time for your data.” If you set a partition expiration, Google's rule is: “Make sure the expiration is 14 days or longer, and avoid changing the table schema (for example, adding a column).” A changed schema makes the export fail. Never set an expiration on the whole table, which would delete everything.

Querying the export: SQL that gives correct numbers

The tables are not pre-summed. The same query, date and search type can appear in several rows, so “you should almost always aggregate all your metrics.” Position is stored as a zero-based sum, so the average needs a division and + 1. And every query is billed by the bytes it scans, so filter on the partition column first.

Code · Annotated SQL on the URL table
-- Top non-brand queries per URL, last 28 complete days, web search
SELECT
  url,
  query,
  SUM(clicks)      AS clicks,          -- ① always aggregate: keys repeat across rows
  SUM(impressions) AS impressions,
  SAFE_DIVIDE(SUM(clicks), SUM(impressions)) AS ctr,   -- ② recompute CTR, never average it
  SAFE_DIVIDE(SUM(sum_position), SUM(impressions)) + 1 AS avg_position  -- ③ zero-based, so +1
FROM `my-project.searchconsole.searchdata_url_impression`
WHERE data_date BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
                    AND DATE_SUB(CURRENT_DATE(), INTERVAL 3 DAY)  -- ④ partition filter = lower cost
  AND search_type = 'WEB'
  AND NOT is_anonymized_query                           -- ⑤ drop the empty-query rows
  AND NOT REGEXP_CONTAINS(query, r'example|exmple')        -- ⑥ your brand variants
GROUP BY url, query
HAVING SUM(impressions) >= 50
ORDER BY clicks DESC
LIMIT 1000;
Six habits make the numbers match Search Console: aggregate, recompute ratios, add one to position, filter by date, handle anonymized rows, and define brand explicitly.

The notes in detail:

  1. Aggregate. Google's own example shows why: a plain SELECT query, impressions returns one fragment of a query's traffic, while SUM(impressions) … GROUP BY query returns the total.
  2. Recompute ratios. CTR is SUM(clicks) / SUM(impressions) over the rows you group, never an average of per-row CTRs.
  3. Position. Use SUM(sum_position)/SUM(impressions) + 1 on the URL table and SUM(sum_top_position)/SUM(impressions) + 1 on the site table.
  4. Date filter. Google advises using “a WHERE clause to limit the date range in the date partitioned table”. A query without it scans the full history every time.
  5. Anonymized rows. “Anonymized queries often make up the single most common query for a site.” Leave them out of top-query lists, but keep them in totals when you compare with the Performance chart.
  6. Search type values. Google's sample queries use upper-case values such as 'WEB' and 'DISCOVER', and lower-case country codes such as 'usa'.

Choose the table by question. Sitewide totals, query trends and country or device splits come from searchdata_site_impression. Anything per URL, per search appearance or for Discover comes from searchdata_url_impression. Never add the two together: they count the same impressions differently.

Looker Studio connector: site vs URL impression

Looker Studio has a free, native Search Console connector. Google's current documentation calls the product Data Studio again, so you will see both names. The connector is the shortest route from Search Console to a shareable dashboard, and it can sit next to Google Analytics, Google Ads or BigQuery data in the same report.

To connect: create a data source, choose the Search Console connector, authorize, pick the property, then choose the table and default search type. You must be signed in with an account that has at least view permission on the property.

Two tables, one per data source

The connector exposes the same two aggregations as the bulk export. “The Search Console connector gives you access to both these methods however a single data source can only use one of these.”

For both views in one dashboard, Google's advice is: “To see your site impressions and URL impressions side by side, create two data sources and add both to a single report.” Label each chart with its table, because a Site Impression total and a URL Impression total for the same period will not match.

Blank queries and row limits

Privacy filtering shows up as an empty query: “In this case, Data Studio may aggregate these queries into a single row with a blank value.” Filter out null values of the Query dimension in tables of top queries. The connector also uses the same data access as the API, so very large sites hit the same daily row ceiling. When that happens, point Looker Studio at the BigQuery tables instead of the connector.

Credentials

Every data source runs on one of three credential types. “OWNER'S CREDENTIALS let other people view or create reports that use this data without requiring them to have their own access to the dataset.” Viewer's credentials require each viewer to have their own access; service account credentials use a non-human account. With owner's credentials, sharing the report is sharing the data, so treat the share list as an access list.

Which option to use when

Your situationUseWhy
One-off check of top queries or pagesUI exportNo setup, enough rows for most small and medium sites.
Weekly report for a client or managerLooker Studio connectorRefreshes itself, shareable, no code.
Your own archive beyond the report windowAPI daily pull, or bulk exportNeither the UI nor the connector keeps history longer than the report.
More than 1,000 rows, under the daily ceilingAPIFree, filterable, scriptable.
Large site, long tail, joins with crawl or CMS dataBulk exportNo row ceiling, raw rows, SQL.
Dashboards for a large siteBulk export + Looker Studio on BigQueryComplete data in the charts; watch query cost.
Index status of specific URLsURL Inspection APISeparate method with its own quota (2,000 a day per site).

A practical signal: if your daily API pull for one search type regularly reaches tens of thousands of rows, you are near the ceiling and the bulk export starts to pay for itself. If it returns a few thousand rows, the API already gives you everything that is not anonymized.

Automation ideas

A health check on the export log can be this short:

SELECT data_date, namespace, MAX(epoch_version) AS version, MAX(publish_time) AS published
FROM `my-project.searchconsole.ExportLog`
WHERE data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 10 DAY)
GROUP BY data_date, namespace
ORDER BY data_date DESC;  -- expect two namespaces per day

Failed exports are not written to ExportLog, so a missing day in this result is your alert. The settings page in Search Console shows the error itself.

Access and privacy hygiene

Search data shows what people type to find you and which pages earn money. Treat every export route as an access decision.

  1. Use a dedicated service account for scripts. Create it in Google Cloud, add its e-mail as a user in Search Console, and give it Restricted permission when it only reads performance data. Do not run scheduled jobs on a person's account.
  2. Request the read-only scope. webmasters.readonly is enough for the Search Analytics method. The full webmasters scope can also change sitemaps and properties.
  3. Keep keys out of code. Store service account keys in a secret manager, not in a repository, a shared drive or a spreadsheet. Rotate them and delete unused ones; where your platform allows keyless authentication, prefer it.
  4. Grant only the documented roles. Search Console's export account needs BigQuery Job User and BigQuery Data Editor, nothing more. Analysts usually need read access to the dataset and permission to run jobs, not project ownership.
  5. Choose Looker Studio credentials deliberately. Owner's credentials expose the data to everyone the report is shared with. For sensitive properties, use viewer's credentials.
  6. Review users regularly. Remove former employees, agencies and old service accounts from Search Console, the Cloud project and shared reports.

Put your performance data next to a technical crawl

OurSEO Tools crawls up to 1,000 pages and records status code, canonical, indexability and load time for every URL, the fields you will want to join with your Search Console page data.

69-point checklistTechnical crawlContent analysis

Common mistakes

FAQ

Is the Search Console API free?

Yes. There is no charge for the Search Console API itself; you are limited by quotas and the daily row ceiling. Costs appear only with the bulk export, because BigQuery storage and queries are billed to your Google Cloud project.

Can I get data older than about 16 months?

Not from Google. The API reads the same window as the Performance report, and the bulk export starts on the day you set it up. The only way to keep longer history is to archive it yourself, starting now.

Why are my API totals lower than the Performance chart?

The API has no row for anonymized queries, returns only top rows when you group by page or query, and stops at about 50,000 rows per day per search type. For exact totals, query without the page and query dimensions.

Does the bulk export include anonymized queries?

Their clicks and impressions are included, but the query text is not: those rows have is_anonymized_query set to true and an empty query string.

Should I use the Looker Studio connector or BigQuery as the dashboard source?

The connector for small and medium sites: it is free and needs no setup. BigQuery when you hit the row ceiling, need joins with other data or want history beyond the report window.

Who can set up the bulk export?

Only verified owners of the Search Console property, and they also need access to a Google Cloud project with billing where they can grant roles to Search Console's export account.

Sources

Quotes checked against the official pages on 30 October 2026.