Chrome storage quota checker
Paste the JSON object that your extension writes to chrome.storage. The checker counts the bytes that Chromium counts against the quota. For each key, it adds the UTF-8 bytes of the key to the bytes of the value written by Chromium's own JSON writer. It then compares the totals with the limits of chrome.storage.sync and chrome.storage.local. It does not count chrome.storage.session, because Chromium measures that area by estimated memory use. It does not read the storage of an installed extension.
Everything runs in your browser. Nothing is uploaded. The counting rules and the limits come from the Chromium source code. The table below gives each limit from the source and the commit that it was fetched from.
Check your storage payload
How the bytes are counted
- Per item. Chromium's
SettingsStorageQuotaEnforcer::Allocatecomputeskey.size() + value_as_json.size(), wherevalue_as_jsonis the value written throughbase::WriteJson. A key is counted once, without quotes, as its raw UTF-8 length. - Replacing. When you set a key that is already stored, its new size replaces its old size.
Allocateaddsnew_size - existing_sizeto the total. The checker counts the pasted object only, so add the keys that are already stored if they stay. - Strings. Chromium escapes
\b \f \n \r \t \\ \", writes<as\u003C, writes U+2028 and U+2029 as\u2028/\u2029, and writes other control characters as\uXXXX. Everything else, including non-ASCII characters, passes through as UTF-8 bytes. The checker counts UTF-8 bytes, matchingstd::string::size(). - Numbers. A number from -2147483648 to 2147483647 with no fraction is an int32. Chromium writes it as plain digits, for example
42. JSON1.0is the JavaScript number 1, so it counts as the int321. Each other number is a double. Chromium writes a double in its shortest form. It uses an exponent when the exponent is below -6 or is 12 or more, for example1e+12and1.761886338123e+12. When the result has no.and noe, Chromium adds.0, for example3000000000.0. For large numbers, JavaScript uses an exponent only from 1e21. SoJSON.stringifygives a different length for these numbers. - Booleans and null.
true,falseandnullcount 4, 5 and 4 bytes. - Negative zero. JSON
-0is the JavaScript number -0.argument_spec.cccallsSetConvertNegativeZeroToInt(true)when it converts the values ofset(), so the checker counts-0like0. This page did not test a real write with-0, so it gives no exact count for it. - Values that do not convert. A top-level key whose value is not a finite number (JSON such as
1e999, which is too large for a double) is skipped byargument_spec.cc, soset()does not store it. The checker lists that key and does not count it. - Enforcement. Chromium rejects a write only when the new total is greater than the limit, or one item is greater than the per-item limit, or the item count is greater than
MAX_ITEMS. A count equal to its limit passes. - Split plan. For a value above the per-item limit, the checker cuts
JSON.stringify(value)into strings stored underkey_0,key_1and so on. Each piece is as long as it can be while its key bytes plus its escaped string bytes stay at or below 8192.
The limits in the Chromium source
From extensions/common/api/storage.json and local_value_store_cache.cc, Chromium commit 2e8cd78a59207e3ab95eeb073fb3a194498dab93, fetched 2026-10-31.
| Area | Limit | Value in the source | Notes from the source |
|---|---|---|---|
| sync | QUOTA_BYTES | 102400 (100 KB) | Total across all keys. |
| sync | QUOTA_BYTES_PER_ITEM | 8192 (8 KB) | Per key, including the key length. |
| sync | MAX_ITEMS | 512 | Maximum number of keys. |
| sync | Write operations | 1800 / hour, 120 / minute | Not measured by this checker; MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE is deprecated. |
| local | QUOTA_BYTES | 10485760 (10 MB) | Ignored when the extension has the unlimitedStorage permission: WeakUnlimitedSettingsStorage passes every write through. |
| session | QUOTA_BYTES | 10485760 (10 MB) | In memory. The source measures it by "estimating the dynamically allocated memory usage" of every value and key, not by JSON size. This checker does not count session. |
| managed | n/a | n/a | Read-only for the extension; set by enterprise policy. |
Sources
Each rule of the checker comes from one of these files. The Chromium files are at commit 2e8cd78a59207e3ab95eeb073fb3a194498dab93. All files were fetched on 2026-10-31.
- chrome.storage API reference (last updated 2026-10-29). It says "The quota limitation is approximately 100 KB, 8 KB per item". The Chromium source gives exact byte numbers, so the checker uses the source.
- storage.json gives the values of
QUOTA_BYTES,QUOTA_BYTES_PER_ITEM,MAX_ITEMSand the write limits. - settings_storage_quota_enforcer.cc gives the item size
key.size() + value_as_json.size(). A write fails only when a count is greater than its limit. - local_value_store_cache.cc gives local a total limit only. With
unlimitedStorage, every local write goes through. - json_writer.cc adds
.0to a double that has no.and noe. - string_number_conversions_internal.h writes the shortest form of a double. It uses an exponent when the exponent is below -6 or is 12 or more.
- string_escape.cc gives the escapes of strings.
- v8_value_converter_impl.cc makes int32 numbers integers and other finite numbers doubles. It does not convert a number that is not finite.
- argument_spec.cc converts the items of
set(). It keepsnullvalues (preserveNullin storage.json), skips a top-level key whose value does not convert, and callsSetConvertNegativeZeroToInt(true).
Where a real write can still differ
- The
sessionarea counts estimated memory use, not JSON size. So this checker gives no session count. - Chromium changes values that JSON cannot hold before it counts them. In an object, it drops a property whose value it cannot convert, for example a function or a number that is not finite. In an array, it writes
nullfor that value. This checker reads JSON text, so it sees only the non-finite numbers of this list. - A
Datebecomes an empty object. storage.json says that aDateand aRegExpserialize "using their String representation". The source does not agree.v8_value_converter_impl.ccconverts both as objects unless dates and regular expressions are allowed, andargument_spec.ccdoes not allow them. - The converter stops at a nesting depth limit (
kMaxRecursionDepthis 100 inv8_value_converter_impl.cc) and does not convert values below that depth. The checker does not model this limit. It shows a note when a value is nested 100 levels or more.
Limits of this checker
Does the checker upload my JSON?
No. The page parses and measures the text in your browser with JavaScript. It sends nothing over the network.
Why does my byte count differ from JSON.stringify(...).length?
There are three common reasons. First, .length counts UTF-16 code units, while Chromium counts UTF-8 bytes; a character outside the BMP counts 4 UTF-8 bytes but 2 code units. Second, Chromium escapes < and the line/paragraph separators, which a plain JSON.stringify does not. Third, Chromium writes each number that is not an int32 as a double, for example 3000000000.0 and 1e+12. Two rare cases also differ. Chromium writes an unpaired surrogate as U+FFFD (3 bytes), and it drops an object property whose number is not finite. Use the checker's per-item table to see the exact text.
Does it simulate the write-op budgets?
No. The limits of 1800 writes per hour and 120 writes per minute apply to chrome.storage.sync only, and they count calls, not bytes. The limits table shows them for reference. The checker does not model call counts. To test the match patterns of a manifest, use the match pattern tester.