# bigrquery gives R three ways into BigQuery, from raw REST calls to dbplyr verbs

> bigrquery is the tidyverse's R interface to Google BigQuery, offering a low-level API of bq_ functions, a DBI connection, and a dbplyr layer that treats a BigQuery table as an in-memory data frame. It suits R analysts who want to work against warehouse-scale tables without leaving the language, and its three abstraction layers differ in more than convenience.

**r-dbi/bigrquery** — An interface to Google's BigQuery from R.

- Repository: https://github.com/r-dbi/bigrquery
- Website: https://bigrquery.r-dbi.org
- Stars: 531 · Forks: 190
- Language: R
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/r-dbi-bigrquery

## Three abstraction levels, and picking the right one

The README describes bigrquery as providing three levels of abstraction on top of BigQuery, and the choice between them is the first decision a user makes.

The low-level API provides thin wrappers over the underlying REST API. All the low-level functions start with bq_, and mostly have the form bq_noun_verb(). The README says this level is most appropriate if you are familiar with the REST API and want to do something not supported in the higher-level APIs. In practice that means functions like bq_project_query, bq_table_download and bq_table_delete, and it means you are the one assembling the request.

The DBI interface wraps the low-level API and makes working with BigQuery like working with any other database system. The README calls this the most convenient layer if you want to execute SQL queries in BigQuery or upload smaller amounts of data, that is under 100 MB. This is the layer that gives you a connection object with dbConnect, dbListTables and dbGetQuery, so an existing R script that talks to any database will work against BigQuery with a changed connection string.

The dplyr interface lets you treat BigQuery tables as if they are in-memory data frames. The README calls it the most convenient layer if you do not want to write SQL, but instead want dbplyr to write it for you. That is the tbl() and dplyr verb layer, and it is the one where the query is generated lazily and pushed down to BigQuery.

The three layers are not three packages. They are one package, and the naming convention of the low-level functions is what lets you stay at one level when you need to.

The package is published on CRAN as bigrquery, its homepage is bigrquery.r-dbi.org, and its README is generated from README.Rmd, so the source of truth for the documentation is an R Markdown file rather than the Markdown file itself.

## The low-level path, where a query becomes a tibble

The low-level example in the README is short enough to show the whole shape of the workflow.

```r
library(bigrquery)
billing <- bq_test_project() # replace this with your project ID 
sql <- "SELECT year, month, day, weight_pounds FROM `publicdata.samples.natality`"

tb <- bq_project_query(billing, sql)
bq_table_download(tb, n_max = 10)
```

Three things are happening. A billing project is established, here through bq_test_project() with a comment reminding you to replace it with your own project ID. A query is issued with bq_project_query, which returns a table object rather than data, in the same way a database driver returns a result handle. And the rows are fetched with bq_table_download, with n_max = 10 limiting the download.

That separation between running a query and downloading its results is the important structural point. BigQuery is a warehouse, so a query returns a reference to a result set and the data transfer is a separate, explicitly sized step. The n_max argument is how you bound that step, and it is the argument that keeps a test from pulling a hundred million rows into an R session.

The same pattern holds for uploads. bq_table_upload() is one of the functions named in the README, and it appears in the discussion of the privacy policy, which is the point at which the low-level API becomes the right level of choice.

Because the low-level layer is thin, everything about a request is explicit, and that is also its cost. A user working at this level is responsible for the shape of the call, and the convenience of the higher layers is exactly what they have traded away.

## DBI and dplyr: the same table two different ways

The two higher layers are shown in the README against the same public dataset, and comparing them side by side is the clearest way to see what each buys.

The DBI version opens a connection and then queries it:

```r
library(DBI)

con <- dbConnect(
  bigrquery::bigquery(),
  project = "publicdata",
  dataset = "samples",
  billing = billing
)
```

The connection object prints its dataset and billing project, and dbListTables(con) returns the tables in that dataset by name, including natality, gsod, shakespeare and wikipedia. dbGetQuery(con, sql, n = 10) then runs the SQL and returns a tibble.

The dplyr version reaches the same table without writing SQL:

```r
library(dplyr)

natality <- tbl(con, "natality")

natality %>%
  select(year, month, day, weight_pounds) %>% 
  head(10) %>%
  collect()
```

The tbl() call binds a remote table to a lazy dplyr pipeline, and collect() is what actually pulls the rows down. The head(10) in the pipeline is not executed in R, it becomes part of the generated SQL, which is the whole point of the layer.

One detail in the dplyr output is worth a second look. The first ten rows of the dplyr example have NA in the day column and different years from the SQL example, because the two queries returned different orderings of the same table, and neither example has an ORDER BY. That is a real characteristic of the layer rather than a defect in the example, and it is the kind of thing that surprises people moving from local data frames, where row order is stable.

The practical division of labour between the two is that DBI is for writing SQL by hand, and dplyr is for when you would rather not. Both end in a tibble, and both are backed by the same low-level API.

## A project, a billing project, and the free tier to learn on

The Important details section of the README opens with the practical setup question, and the answer is that you need a BigQuery project.

For learning, the README points at Google's free public data and the BigQuery sandbox, which gives you some data to play with along with enough free compute, 1 TB of queries and 10 GB of storage per month, to learn the ropes. That is a generous allowance for trying the package, and it removes the need to have a paid BigQuery account before you have written a line of R.

The setup step is to open the BigQuery console and create a project, then make a note of the Project ID. The README is careful about how that identifier is used, and this is the detail that catches people out: you use it as the billing project whenever you work with free sample data, and as the project when you work with your own data. The same identifier means two different things depending on which dataset you are pointing at, and the example code demonstrates it. In the low-level snippet, bq_test_project() stands in for the billing project, and in the DBI connection the project is set to publicdata, the dataset to samples, and billing to the same value.

So the billing project is the one charged for query compute, and the project and dataset arguments identify the data. When you query somebody else's public dataset, the cost lands on your billing project, and when you query your own data, the data project is the one that matters for permissions and for where the results live.

Getting this wrong is not a syntax error, it is an unexpected bill, so it is worth setting the two apart before you run anything large.

## Authentication, and the difference between interactive and scheduled use

Authentication is handled by gargle, and the README documents two situations with different answers.

Interactively, you are prompted to authorize bigrquery in the browser, and you are asked whether you want to cache tokens for reuse in future sessions. That cached token is what makes the second run of an R session not require a browser round trip, which matters when you are iterating on a query.

For non-interactive usage, the README says it is preferred to use a service account token if possible. That is the case that matters for anything scheduled, since there is nobody there to click through a consent screen. The README links the gargle documentation on how tokens are fetched, on non-interactive auth, and on getting your own API credentials, and notes that bigrquery obtains its token with gargle::token_fetch(), which supports a variety of token flows including Application Default Credentials and service accounts on GCE VMs.

The relevant reference for the package itself is bq_auth(). The README points at its help page, which is where the credential selection logic for this particular package is documented.

The privacy note is short and worth reading carefully. bigrquery requests permission to modify your data, but it will never do so unless you explicitly request it, for example by calling bq_table_delete() or bq_table_upload(). The permission is granted at the OAuth level because the API surface requires it, and the package's own discipline is that nothing is modified without a deliberate call. The privacy policy is linked from that section.

## Where bigrquery is the wrong tool

There are limits, and they come from the shape of BigQuery rather than from the quality of the package.

The first is the upload ceiling. The README is explicit that the DBI layer is the convenient choice for uploading smaller amounts of data, i.e. under 100 MB, and that is a description of the layer's convenience rather than a hard limit. For bulk loading, BigQuery's own job mechanisms exist and the low-level bq_ functions are the route to them, but at that point you are writing API calls rather than using the DBI convenience, and a dedicated ETL tool is usually the better answer.

The second is the laziness trap. The dplyr layer looks like local data frame manipulation and is not. Every verb is translated into SQL and the query runs in BigQuery, which means a filter that looks cheap in R can scan a full table and cost real money against your 1 TB monthly allowance. Collecting early with head() or collect() is what bounds the cost, and the fact that the examples return tibbles rather than lazy objects is the reminder.

The third is the third-party alternative. If your pipeline is already in Python, the Google BigQuery Python client gives you the same warehouse with the same cost model, and pairing it with pandas or polars gives the lazy in-memory experience that dbplyr provides in R. The difference in approach is that the Python side has one package rather than three layers, so there is less choice about how abstract to be, and less to learn when you only need the one obvious route.

The fourth is scale of the code base. The repository has a revdep/ directory, an inst/ directory, a src/ directory for the compiled parts, a large man/ directory of documentation, a tests/ directory and a _pkgdown.yml for the site. That is the footprint of a package that has been maintained for years and takes CRAN review seriously, and it comes with the usual R package consequence: a dependency graph that has to resolve before anything runs.

For the specific case of one analyst pulling a table into a notebook to look at it, none of this matters. bigrquery is the right tool when R is where your code already lives and BigQuery is where your data already lives, and the two layers above the raw API mean you can choose how much SQL you want to write.

## Conclusion

Adopt bigrquery if your work is in R and your data lives in BigQuery, since the package covers the whole range from raw API calls through a standard DBI connection to dbplyr verbs without SQL, and it is the maintained tidyverse path rather than a wrapper you maintain yourself. Do not reach for it if you only need a one-off export, because a client library is the wrong shape for that, and note the upload ceiling, since the DBI layer is described as the convenient choice for smaller amounts of data, under 100 MB. Before you commit, set up a project and work out the billing project separately from the data project, since the README is explicit that the billing project is used whenever you work with free sample data. The licence is a custom one that GitHub cannot classify, with terms in the LICENSE file, the latest release is v1.6.2 from 2026-05-13, and the last push was on 2026-05-13.

## FAQ

### What is bigrquery and what does it do?

bigrquery is an R package for working with data stored in Google BigQuery. It lets you query BigQuery tables and retrieve metadata about your projects, datasets, tables and jobs, through a low-level REST wrapper, a DBI connection, or a dbplyr interface.

### How do I install bigrquery?

The current release installs from CRAN with install.packages("bigrquery"). The newest development release comes from GitHub with pak::pak("r-dbi/bigrquery"), and the README notes that the pak package itself is commented out in that example.

### What is the difference between the three bigrquery interfaces?

The low-level API provides thin wrappers over the REST API with bq_ prefixed functions of the form bq_noun_verb(). The DBI interface wraps it so you can run SQL through a database connection, and the dplyr interface lets you treat a BigQuery table as a lazy in-memory data frame with dbplyr writing the SQL.

### How does bigrquery authenticate, and what should I use for scheduled jobs?

Interactively you are prompted to authorize bigrquery in the browser and can cache tokens for later sessions. For non-interactive usage the README says it is preferred to use a service account token, and bigrquery obtains its token through gargle::token_fetch(), which supports Application Default Credentials and service accounts on GCE VMs.

### What is the difference between the billing project and the project in bigrquery?

The README says to create a project and note its Project ID, then use it as the billing project whenever you work with free sample data and as the project when working with your own data. The billing project is charged for query compute, so confusing the two shows up as an unexpected bill.

### How much data can I upload with bigrquery?

The README describes the DBI interface as the convenient layer for uploading smaller amounts of data, i.e. under 100 MB. For bulk loading the low-level bq_ functions or BigQuery's own job mechanisms are the route, and a dedicated ETL tool is usually a better fit.

## Sources

- [Issues](https://github.com/r-dbi/bigrquery/issues)
- [Project website](https://bigrquery.r-dbi.org)
- [r-dbi/bigrquery on GitHub](https://github.com/r-dbi/bigrquery)
- [README](https://github.com/r-dbi/bigrquery/blob/main/README.md)
- [Releases](https://github.com/r-dbi/bigrquery/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/r-dbi-bigrquery
