How to display Google Sheets data in WordPress (free plugin)
A price list, a schedule, a team directory, a table of results: this kind of content changes often, and the person who changes it is rarely the person who logs in to WordPress. Keeping it in a Google Sheet and showing the sheet on the site solves that. The editor updates a cell, the page shows the new value, and nobody touches the WordPress admin.
This guide uses the free SheetDB plugin from the WordPress directory. It renders rows from a Google Sheet through a shortcode, so it works in posts, pages, widgets and page builders, and it needs no code, no OAuth and no Google Cloud project. You will install it, show a sheet as a table, a list and a single value, filter rows with shortcode attributes, send data from a WordPress form back to the sheet, and see how caching and the alternatives (iframe embed, Zapier) compare.
In short: connect Google Sheets to WordPress in three steps
- Create a free SheetDB API from your spreadsheet URL.
- Install and activate the free SheetDB plugin from Plugins → Add New Plugin.
- Add a Shortcode block with your API URL;
{{column}}placeholders are replaced with the values of each row.
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40"]
{{id}} – {{name}}<br>
[/sheetdb]
The examples read a real spreadsheet, this one, exposed at https://sheetdb.io/api/v1/58f61be4dda40. Its first tab has the columns id, name, age and comment. The live results below each shortcode use the same markup the plugin generates.
Install the SheetDB WordPress plugin
- Sign in to SheetDB with the Google account that can open the spreadsheet, click Create new API, paste the spreadsheet URL and save. You get an API URL in the form
https://sheetdb.io/api/v1/<your id>. The spreadsheet stays private; SheetDB reads it with your account. The free plan is enough for a typical site. - In the WordPress dashboard go to Plugins → Add New Plugin, search for SheetDB, click Install Now and then Activate. You can also download it from wordpress.org/plugins/sheetdb and upload the zip under Plugins → Add New Plugin → Upload Plugin.
- Open a post or page, add a Shortcode block (or paste into the classic editor) and enter the shortcode from the next section with your API URL. Preview the page: the rows appear.
There is no settings page. Everything is configured in the shortcode itself, which means you can show different sheets, tabs and filters on different pages of the same site. The plugin outputs the same markup as the SheetDB HTML snippet and loads the small script that fetches the rows in the visitor's browser, so nothing is stored in your WordPress database and the theme is untouched.
Shortcode examples: table, list, single cell
The content between [sheetdb] and [/sheetdb] is a template. It is repeated once per row of the sheet (the first row is the header and is skipped), and {{column_name}} is replaced with the value of that column. The simplest form:
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40"]
{{id}} – {{name}}<br>
[/sheetdb]
Table
Use the element attribute to choose the wrapper the rows are rendered into. For a table, write the header in plain HTML, set element="tbody" and use one <tr> as the row template. This one sorts by age, oldest first:
<table>
<thead>
<tr><th>Name</th><th>Age</th><th>Comment</th></tr>
</thead>
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40" element="tbody" sort-by="age" sort-order="desc"]
<tr><td>{{name}}</td><td>{{age}}</td><td>{{comment}}</td></tr>
[/sheetdb]
</table>
| Name | Age | Comment |
|---|---|---|
| {{name}} | {{age}} | {{comment}} |
List
For a bullet list set element="ul" and put an <li> in the template. limit caps the number of rows, which combined with sorting gives you a "top 3" list:
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40" element="ul" limit="3" sort-by="age" sort-order="asc"]
<li>{{name}} is {{age}} years old</li>
[/sheetdb]
- {{name}} is {{age}} years old
Single cell
To show one value inside a sentence, for example a price or a name that changes, filter down to one row with search and render an inline element. The rest of the sentence is ordinary text in the editor:
Our special guest is [sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40" element="span" search="id=4"]{{name}}[/sheetdb].
Two things about the template. {{name}} renders plain text: any HTML stored in the cell is escaped, so a visitor cannot inject markup through the sheet. If you want HTML from a cell to render, for example a link or bold text an editor typed into the spreadsheet, use {{html:name}}; the plugin keeps safe tags such as a, p, ul, strong and img and strips script, iframe, object, inline on* handlers and javascript: links. And the tab is chosen with sheet="Tab name"; without it the first tab of the spreadsheet is used.
Filter rows with shortcode attributes
url is the only required attribute. The others map to the parameters of the SheetDB read and search endpoints and are all optional:
search– show only rows that match, ascolumn=valuepairs joined with&. Values accept*as a wildcard (name=Ste*),!for negation (comment=!special) and>,<,>=,<=for comparisons (age=>18). All conditions must match.limitandoffset– how many rows to render and how many to skip, for a "latest 5" list or manual pagination across pages.sort-byandsort-order– the column to sort by andascordesc.sheet– the tab of the spreadsheet to read.element– the HTML element that wraps the rendered rows; defaults todiv. Usetbody,ul,ol,spanor any other tag that fits the template.lazy-loading="true"– fetch the rows only when the shortcode scrolls into view, useful for a long table at the bottom of a page.save– keep the fetched rows under a name so a[sheetdb-slot]elsewhere on the page can reuse them without a second request (see below).
A filtered list, showing adults whose comment is "special":
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40" element="ul" search="age=>18&comment=special"]
<li>{{name}} ({{age}}) – {{comment}}</li>
[/sheetdb]
- {{name}} ({{age}}) – {{comment}}
To reuse a row in several places, for example the name of this week's featured product in the header and its description further down, fetch it once with save and render it again with a slot:
[sheetdb url="https://sheetdb.io/api/v1/58f61be4dda40" search="id=4" save="guest"]
<h3>{{name}}</h3>
[/sheetdb]
<!-- later on the same page, no extra request -->
[sheetdb-slot slot="guest"]{{name}} is {{age}} years old.[/sheetdb-slot]
The search attribute calls the search endpoint, so keep the Search (GET) permission enabled on APIs you filter this way. The current list of attributes is on the plugin page; the underlying script is documented in the HTML snippet docs.
Send WordPress form data to Google Sheets
The plugin reads. To go the other way, from WordPress to Google Sheets, post to the same API URL. Every column of the sheet is a field of the request, so a plain HTML form does the job: add a Custom HTML block to the page and paste this, replacing the URL with your own API. The field names use the data[column] convention SheetDB expects, and the hidden id field asks SheetDB to fill in the next number with INCREMENT.
<form id="sheetdb-form" method="post" action="https://sheetdb.io/api/v1/58f61be4dda40">
<label>Name <input type="text" name="data[name]" required></label>
<label>Age <input type="number" name="data[age]" required></label>
<label>Comment <input type="text" name="data[comment]"></label>
<input type="hidden" name="data[id]" value="INCREMENT">
<button type="submit">Send</button>
</form>
<script>
const form = document.getElementById('sheetdb-form');
form.addEventListener('submit', (event) => {
event.preventDefault();
fetch(form.action, { method: 'POST', body: new FormData(form) })
.then((response) => {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response.json();
})
.then(() => {
form.reset();
alert('Thank you, your entry was saved.');
})
.catch((error) => alert('Something went wrong: ' + error.message));
});
</script>
Submissions land as new rows at the bottom of the tab, within a second. The HTML form to Google Sheets guide has variants of this form (redirect after submit, validation) and the create docs list the options, such as DATETIME for a timestamp column. If you already use a form plugin that can send submissions to a URL (a webhook or "send to external URL" option), point it at the API URL with a JSON body whose keys are the column names: {"data": {"name": "…", "age": "…"}}.
Two settings matter here. In the SheetDB API settings, keep Create (POST) enabled for the API the form posts to, and disable Update (PATCH/PUT) and Delete (DELETE), so a visitor who finds the URL in the page source can add a row but not change or remove one. If the same API also displays data, Read (GET) and Search (GET) stay on; if you prefer, create a second API for the same spreadsheet that only writes.
Caching and performance
Nothing is copied into WordPress: each page view fetches the rows in the visitor's browser, directly from SheetDB. Between the sheet and your visitors sits the SheetDB cache, which stores read responses for 15 seconds by default. Google limits how often a spreadsheet can be read, and a page with a few thousand views an hour would exhaust that limit quickly; with the cache, only one request per cache period reaches Google, everything else is served from the cache. Writes made through the API clear the cache immediately, and manual edits in the sheet appear within the cache duration. For a table that changes once a day, raise the cache duration to several minutes in the API settings; for a live scoreboard leave the default.
Each shortcode makes one request per page view and counts against the monthly quota of your plan. Three habits keep the number low: use search and limit instead of loading a whole tab and showing part of it, use save and [sheetdb-slot] when one row appears in several places, and add lazy-loading="true" to shortcodes below the fold so they only load when someone scrolls to them. WordPress caching plugins do not interfere: they cache the page HTML, and the rows are fetched afterwards in the browser.
Because the API URL is visible in the page source, make the API read-only unless a form needs to write: in the API settings keep Read (GET) and Search (GET) enabled and switch off Create (POST), Update (PATCH/PUT) and Delete (DELETE). Anyone can read what the page shows anyway; nobody should be able to change it.
Alternatives: embed iframe vs plugin vs Zapier
- Google's iframe embed. In the sheet, File → Share → Publish to web gives you an
<iframe>you can paste into a Custom HTML block. It takes a minute and needs no account, but the result looks like a spreadsheet grid, cannot be styled to match the theme, is hard to read on phones, and the whole sheet has to be published publicly. Good for a quick internal page, not for a customer-facing one. - The SheetDB plugin (this guide). The rows are rendered into your own HTML, so the table or list looks like the rest of the site and you choose which columns appear. The spreadsheet stays private, filtering and sorting happen on the API side, and the same API can feed other pages or apps. It needs a free SheetDB account and the shortcode syntax.
- Zapier, Make or n8n. Automation tools copy data between Google Sheets and WordPress, for example creating a post per row or a row per form entry. They are the right choice when the data should become WordPress content (posts, products) rather than be displayed live, and for pushing form entries into a sheet when you cannot edit the page HTML. Each run costs a task, changes appear with a delay, and you maintain a copy of the data in two places. The n8n and Make guide shows how those tools call the same API.
- Plugins that use Google's API directly. Several table plugins can import a Google Sheet. They usually require a public sheet or credentials from a Google Cloud project, store a copy in WordPress and refresh it on a schedule. Fine when you want the data inside WordPress; heavier to set up than a shortcode.
FAQ
Is the SheetDB WordPress plugin free?
Yes. The plugin is free and open source in the wordpress.org directory, and it works with the SheetDB free plan, which has a monthly request quota and needs no credit card. Every shortcode on a page makes one request per page view; slots let several shortcodes share one request.
Do I need to make my Google Sheet public to show it in WordPress?
No. SheetDB reads the spreadsheet with the Google account you signed in with, so the sheet stays private. Only the SheetDB API URL is public, it returns the tabs you chose, and you can restrict it to read-only in the API settings.
Can visitors edit the Google Sheet from my WordPress site?
The plugin only reads. To let visitors add rows, put an HTML form on the page that posts to the same API URL, as shown in the form section above, and keep the create permission enabled on that API. Leave update and delete disabled unless your own code needs them.
How quickly does the WordPress page update when the sheet changes?
The data is fetched in the visitor's browser on every page view, so nothing is stored in WordPress. SheetDB caches read responses for 15 seconds by default and you can raise the cache duration in the API settings; a manual edit in the sheet appears on the site within that time, and writes made through the API clear the cache immediately.
Can I embed a Google Sheet in WordPress without a plugin?
Yes. Google's File → Share → Publish to web gives you an iframe you can paste into a Custom HTML block, but it shows the raw grid, cannot be styled and requires the whole sheet to be public. You can also paste the SheetDB HTML snippet into a Custom HTML block; the plugin is that snippet packaged as a shortcode, so you do not have to edit the theme.
Does the plugin work with Gutenberg, Elementor and other page builders?
Anywhere WordPress renders shortcodes: the Shortcode block in the block editor, the classic editor, text or shortcode widgets, and the shortcode or text widgets of page builders such as Elementor. If a builder offers a "shortcode" element, paste the [sheetdb] shortcode there.
Prefer to watch? These two short videos install the plugin and build a first table:
Show your spreadsheet on your WordPress site
Sign in with Google, paste the spreadsheet URL, install the plugin and add one shortcode. Free plan, no credit card, no coding.
Create free APIHave a question?
Ask about the API or your sheet. Developers answer, usually the same day.