Keep a Sheet Live From a URL
Pull JSON from a URL straight into a sheet with one formula. It runs inside your own Google account, so an API's data never passes through us.
- Open the script editor. In your Google Sheet, choose .
- Paste the script. Replace everything in
Code.gswith the script below, then click Save. - Approve it once. With
authorizeselected in the toolbar, click Run, then Review permissions and allow access. Google may say the app isn’t verified: it’s your own script, so choose Advanced, then Go to the project. Formulas can’t ask for this permission themselves, so this step is needed once. - Use the formula. Back in the sheet, type it into any cell:
=IMPORTJSON("https://api.example.com/users")
/**
* Run this once from the editor (select authorize, click Run) and approve
* access. Formulas in cells can't ask for permission on their own.
* It only builds a request; nothing is fetched.
*/
function authorize() {
UrlFetchApp.getRequest('https://example.com');
}
/**
* Imports JSON from a URL into your sheet as a table: one row per
* record, nested fields as dot-notation columns (Address.City).
* From jsonsupport.com.
*
* @param {string} url The URL that returns JSON.
* @param {string} path Optional. Where the records are: "data.items".
* @param {any} refresh Optional. Point this at any cell; changing
* that cell re-fetches the data.
* @return The table, header row first.
* @customfunction
*/
function IMPORTJSON(url, path, refresh) {
if (!url) {
throw new Error('Give IMPORTJSON a URL: =IMPORTJSON("https://...")');
}
const response = UrlFetchApp.fetch(url, {
muteHttpExceptions: true,
headers: { Accept: 'application/json' },
});
const status = response.getResponseCode();
if (status >= 400) throw new Error('The URL returned HTTP ' + status + '.');
let data;
try {
data = JSON.parse(response.getContentText());
} catch (e) {
throw new Error('The URL did not return valid JSON.');
}
if (path) {
for (const key of String(path).split('.')) {
if (data === null || typeof data !== 'object' || !(key in data)) {
throw new Error('"' + path + '" was not found in the response.');
}
data = data[key];
}
} else if (isObject_(data) && !Array.isArray(data)) {
// No path given: APIs often wrap the records, e.g. {"data": [...]}.
const lists = Object.keys(data).filter(key =>
Array.isArray(data[key]) && data[key].some(isObject_));
if (lists.length) {
lists.sort((a, b) => data[b].length - data[a].length);
data = data[lists[0]];
}
}
const records = Array.isArray(data) ? data : [data];
if (records.length === 0) return [['No records']];
const flat = records.map(record => flatten_(record, ''));
const keys = [];
const seen = {};
flat.forEach(row => Object.keys(row).forEach(key => {
if (!seen[key]) { seen[key] = true; keys.push(key); }
}));
const header = keys.map(key => key.split('.')
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
.join('.'));
const rows = flat.map(row => keys.map(key =>
(row[key] === undefined || row[key] === null ? '' : row[key])));
return [header].concat(rows);
}
function isObject_(value) {
return value !== null && typeof value === 'object';
}
function flatten_(value, prefix) {
if (!isObject_(value)) {
const single = {};
single[prefix || 'value'] = value;
return single;
}
const out = {};
Object.keys(value).forEach(key => {
const flatKey = prefix ? prefix + '.' + key : key;
const child = value[key];
if (Array.isArray(child)) {
if (!child.some(isObject_)) {
// A simple list (tags, ids) becomes one comma-separated cell.
out[flatKey] = child
.map(item => (item === null ? '' : String(item)))
.join(', ');
} else {
child.forEach((item, i) =>
Object.assign(out, flatten_(item, flatKey + '.' + i)));
}
} else if (isObject_(child)) {
Object.assign(out, flatten_(child, flatKey));
} else {
out[flatKey] = child === null ? '' : child;
}
});
return out;
}
More ways to call it
=IMPORTJSON("https://…", "data.items")- Records sit deeper in the response? Name the path. Without one, a wrapper like
{"data": [...]}is found automatically. =IMPORTJSON(A1)- Keep the URL in a cell. Change the cell and the data reloads.
=IMPORTJSON("https://…", , B1)- Refresh on demand: point the third argument at a checkbox and tick it to re-fetch. Sheets doesn't allow NOW() here.
Building this into your own product?
Call the same conversion from your own code with a rate-limited developer API: a free tier to start, Pro for production traffic.
How to Put JSON Into Google Sheets
- Paste or drop your JSON. An array of records, an API response, or JSON Lines all work. Wrapped responses like
{"data": [...]}are unwrapped for you. - Check the table. It appears instantly, with the exact rows and columns Sheets will get, with nested fields flattened into columns like
Address.City. - Copy, then paste into A1. Click Copy for Google Sheets, open your sheet, select cell A1, and paste with Ctrl+V (⌘V on a Mac; on a phone, tap A1 twice and choose Paste). Every value lands in its own cell.
Prefer a file? Download CSV and use in Google Sheets. Copying and the CSV are built in your browser, so that data never leaves your device. Only Download Excel sends the rows to our converter, which builds the file and doesn't save your data.
Questions
Can Google Sheets open a JSON file directly?
No. Sheets imports CSV, TSV, and Excel files, not JSON. Convert it to a flat table first: copy the result above and paste it, or download the CSV and import that.
How are nested objects and arrays handled?
Nested objects become dot-notation columns (Address.City). Arrays of objects, like orders, become one row per item or numbered columns (Orders.0.Sku, Orders.1.Sku), your choice, and simple lists like tags become one comma-separated cell. It's the same logic as the JSON to Excel converter, so both give you identical columns; see both layouts side by side.
Will Sheets change any of my values?
Copy for Google Sheets marks text that Sheets would otherwise rewrite, such as IDs with leading zeros (00123) or text starting with =, so it's kept as text. Real JSON numbers stay numbers you can sum and sort. If you import the CSV instead and need leading zeros, untick "Convert text to numbers, dates, and formulas" in the import dialog.
Does IMPORTJSON work with APIs that need a key?
Yes, if the API accepts the key in the URL, for example ?api_key=…. Keep in mind that anyone who can edit the sheet can see a key written into a formula.
For a longer walkthrough of every method, read how to import JSON into Google Sheets. Working with JSON Lines? See what NDJSON is.