Bulk UUID generator

Up to 10,000 UUIDs in one batch, generated locally, with structured export for seeding a database or a fixture file.

G

What the caps actually are elsewhere

Every UUID tool on the first page of results that we measured generates on a server, which means the batch size is a product decision made by someone else and the rate limit is enforced against your IP address. We probed three of them directly.

uuidtools.com caps a batch at 100 and returns HTTP 429 at the sixty-first request within a minute, verified over 65 sequential calls. generate-random.org caps at 100 with the same 60 requests per minute limit, also verified at request 61. uuidgenerator.net accepts a batch parameter up to 500, and asking for 501 returns ten values with no error and no message: the request is silently reduced by a factor of fifty. If you scripted against that endpoint and did not count the rows, you would not notice.

This tool does 10,000 in the browser. There is no rate limit because there is no server to limit against, and no per-request latency because nothing is requested. That is not a claim about randomness quality; the server-side generators we audited use legitimate CSPRNGs and we are not suggesting otherwise. It is a claim about the architecture: uuidtools states on its own site that it has generated over 1.15 billion UUIDs all time, and every one of them crossed their network.

Measured batch limits, September 2026
ToolBatch capRate limitBehaviour above the cap
uuidtools.com10060 requests per minute per IPEnforced client and server side
uuidgenerator.net500Not measuredReturns 10, silently, with no error
generate-random.org10060 requests per minute per IPClamped
This page10,000NoneCap is stated on screen, never silent

Where the cost actually lands

Generating ten thousand UUIDs is not expensive. The entropy is one buffer, the formatting is a lookup table, and the whole batch takes single-digit milliseconds. Two details do matter if you are writing this yourself.

The first is that crypto.getRandomValues() has a 65,536-byte per-call quota. At sixteen bytes per UUID that is exactly 4,096 values, and asking for more in one call throws QuotaExceededError rather than returning a short buffer. Batches above that have to be chunked. The second is that the expensive part is not the cryptography at all: it is building the strings and putting ten thousand of them into the DOM. Drawing entropy once for the whole batch instead of once per value, and using a 256-entry hex table instead of toString(16).padStart(2, "0") on every byte, is most of the difference.

This page generates in chunks across animation frames with a progress indicator and a cancel button, so the tab stays responsive and a batch you did not mean to request can be stopped. Several tools in this category have no cap at all and simply freeze the tab; a batch you cannot cancel is worse than a batch that is capped.

Export formats, and the SQL INSERT case

A list of UUIDs separated by newlines is rarely the thing you actually wanted. It is one text-editor macro away from what you wanted, and that macro is where the mistakes happen. Of the fifteen generators we audited, none emits a structured format: the best available were a plain uuids.txt download, a comma separator, and one CSV export gated behind a rewarded video advert or a paid plan.

This page emits JSON arrays, JSON objects with an index, quoted CSV, TSV, .env lines, shell and Python and JavaScript array literals, and SQL INSERT statements. The SQL case has a real detail attached to it. If you are seeding MySQL, UUID_TO_BIN takes a second argument: passing 1 swaps the time-low and time-high fields, which was designed to make version 1 values sort. Applying that swap to a version 4 value is harmless but pointless, and applying it to a version 7 value destroys exactly the time ordering you chose v7 for. Pass 0.

On PostgreSQL the cheapest bulk seed does not involve this page at all: gen_random_uuid() over generate_series does the whole job inside the database. Use an exported batch when the identifiers have to exist before the insert, when they are shared across systems, or when they are going into a fixture file.

  • Store as uuid on PostgreSQL, BINARY(16) on MySQL, uniqueidentifier on SQL Server. Never varchar(36).
  • Quote CSV fields on export. A leading equals, plus, minus or at sign is a formula in Excel.
  • Batch INSERT statements rather than emitting ten thousand single-row statements.

Seeding a test database

Bulk identifiers for test data have one property that distinguishes them from bulk random strings: at 122 random bits you do not need deduplication. The chance that a batch of 10,000 version 4 UUIDs contains a repeat is about 9.4e-30. Short codes are different, and a four-character code will collide inside a batch of a few thousand more often than not, but a UUID batch is safe without a uniqueness pass and any tool charging you page-reload latency for one is selling you nothing.

The property you may actually want is the opposite one: reproducibility. A CSPRNG cannot replay a previous run, so a fixture file whose identifiers change on every generation makes diffs unreadable and breaks any assertion that hardcodes an ID. Generate the batch once, commit the output, and treat it as data rather than as something the test suite regenerates. If you need identifiers that are derived rather than stored, a version 5 UUID over a stable name gives the same value every time by construction.

If the test data is going into a table whose primary key is clustered, generate version 7 rather than version 4 so the seed inserts behave like production inserts will. Mixed batches are supported here for exactly that reason: you can produce a v7 primary key column and a v4 external reference column in one pass.

Doing this in your own code

JavaScript
// Draw the entropy once, not once per UUID. getRandomValues has a
// 65,536-byte per-call quota, which is exactly 4,096 UUIDs, so anything
// larger has to be chunked or the call throws QuotaExceededError.
const HEX = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));

function uuidv4Bulk(count) {
  const out = new Array(count);
  for (let base = 0; base < count; base += 4096) {
    const n = Math.min(4096, count - base);
    const bytes = crypto.getRandomValues(new Uint8Array(n * 16));
    for (let i = 0; i < n; i++) {
      const b = bytes.subarray(i * 16, i * 16 + 16);
      b[6] = (b[6] & 0x0f) | 0x40;
      b[8] = (b[8] & 0x3f) | 0x80;
      out[base + i] =
        HEX[b[0]]+HEX[b[1]]+HEX[b[2]]+HEX[b[3]]+"-"+HEX[b[4]]+HEX[b[5]]+"-"+
        HEX[b[6]]+HEX[b[7]]+"-"+HEX[b[8]]+HEX[b[9]]+"-"+
        HEX[b[10]]+HEX[b[11]]+HEX[b[12]]+HEX[b[13]]+HEX[b[14]]+HEX[b[15]];
    }
  }
  return out;
}

A 256-entry hex lookup table removes the per-byte toString(16).padStart(2) call, which dominates the loop at ten thousand values.

SQL
-- PostgreSQL. Generate in the database and you never move the values at all.
INSERT INTO orders (id)
SELECT gen_random_uuid() FROM generate_series(1, 10000);

-- MySQL 8, stored as BINARY(16) rather than CHAR(36).
-- swap_flag 1 reorders the time fields and exists for version 1.
-- For a v4 or v7 value pass 0, or you destroy v7 ordering.
INSERT INTO orders (id) VALUES (UUID_TO_BIN('018f4d1e-...', 0));

-- SQL Server
INSERT INTO orders (id) VALUES (NEWID());

Store 16 raw bytes. A uuid column is 16 bytes; varchar(36) is 37 and compares as text.

Python
import uuid, csv

rows = [uuid.uuid4() for _ in range(10_000)]

with open("ids.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["id"])
    w.writerows([[str(u)] for u in rows])

# Fixtures you want to commit: generate once, write them to the file,
# and never regenerate. A CSPRNG cannot reproduce a previous run.
Shell
# 10,000 UUIDs, one per line
for i in $(seq 10000); do uuidgen --random; done > ids.txt

# Faster, one process
python3 -c "import uuid; print('\n'.join(str(uuid.uuid4()) for _ in range(10000)))"

# As SQL INSERT rows
python3 - <<'PY' > seed.sql
import uuid
print("INSERT INTO orders (id) VALUES")
print(",\n".join(f"  ('{uuid.uuid4()}')" for _ in range(10000)) + ";")
PY

Questions

Why is the cap 10,000?

Because that is roughly where rendering the batch into the page stops being instant on a mid-range phone, and a cap you can feel is better than a cap you discover. The generation itself would go much further. If you need more, generate in successive batches or use the code on this page directly.

Is there a rate limit?

No. Nothing is requested from a server, so there is nothing to limit. Two of the tools we measured return HTTP 429 at the sixty-first request in a minute, which is a reasonable thing for them to do given that they are paying for the compute.

Do I need to deduplicate a bulk UUID batch?

No. A batch of 10,000 version 4 UUIDs has roughly a 9.4e-30 chance of containing a duplicate. Deduplication matters for short codes, not for 122-bit values. This page checks anyway because the check is free at this size, but it has never had anything to report.

Can I get them as SQL INSERT statements?

Yes, with a configurable table and column name, batched into multi-row statements rather than one statement per row. JSON, JSON objects, quoted CSV, TSV, .env lines and language array literals are also available.

Can I mix v4 and v7 in one batch?

Yes. Choose the version per batch, or generate a mixed run when you are seeding a table that has a time-ordered primary key and a separate random external identifier.

Does the batch leave my browser?

No. Open the network tab and generate: nothing is sent. The page is static and the generator is client-side JavaScript you can read, save and run offline.

Related generators