@sampuli/data
Synthetic test data that passes your own validators — 90 countries.
Most fake-data tools give you John Doe and a random 16-digit number. @sampuli/data
gives you a KRA PIN that passes a KRA validator, a real bank's SWIFT/BIC, a
valid IBAN (mod-97), an ICAO-9303 passport MRZ, a Luhn-valid card — each
one shaped to its country's actual rules (checksums, prefixes, bank codes). Entirely
synthetic; corresponds to no real person or account.
npm install @sampuli/data
Try it in your browser, no install: sampuli.site · or in your terminal: npx @sampuli/data ke.person
Why not faker / Mockaroo?
| faker.js | Mockaroo | @sampuli/data | |
|---|---|---|---|
| Country-specific ID / tax formats | ✗ | partial | ✓ 90 countries |
| Checksum-valid (Luhn, mod-97, mod-11…) | ✗ | ✗ | ✓ |
| Real bank names + SWIFT/BIC | ✗ | ✗ | ✓ |
| Valid IBANs, CLABE, NUBAN… | ✗ | ✗ | ✓ |
| Passport MRZ (ICAO-9303) | ✗ | ✗ | ✓ |
| Coherent records (bankSWIFT, gendername) | partial | ✗ | ✓ |
| Reproducible seeds · zero deps · free | ✓ | – | ✓ |
Built for QA and CI at banks and fintechs — test data that passes your own format validation, so you can exercise KYC, onboarding and payment flows without touching real PII.
Older intro / disclaimer
One API, dozens of countries: Kenyan KRA PINs & M-Pesa numbers, Nigerian BVNs, German & Gulf IBANs, Italian Codice Fiscale, Brazilian CPF/CNPJ, Mexican CLABE, Turkish TCKN, Singapore NRIC — and many more.
Every value is entirely synthetic. It matches the real format only and corresponds to no real person, account, or registered number. Never present any output as a genuine ID, tax number, IBAN, or bank account.
From the terminal (CLI)
No code needed — try it with npx:
npx @sampuli/data ke.person # one Kenyan record (JSON)
npx @sampuli/data ke.kra_pin # a single value
npx @sampuli/data ng.person -n 100 # 100 records
npx @sampuli/data de.person -f name,account,card,passport_mrz
npx @sampuli/data ke.person -n 500 --csv # CSV to stdout
npx @sampuli/data ke.preset:kyc # a KYC scenario
npx @sampuli/data --list # list all 90 country codes
npx @sampuli/data --list ke # a country's fields & presets
Quick start
import { generate, generateMany, listPacks } from '@sampuli/data'
listPacks() // every installed country, e.g.
// ['KE','NG','ZA','EG','DE','FR','IT','TR','SA','BR','MX','SG', …]
// One value — the spec is `country.field`:
generate('ke.phone') // Kenya → '0712345678'
generate('ng.bvn') // Nigeria → '22803022407'
generate('de.account') // Germany → 'DE19100900004650895069' (IBAN)
generate('it.id') // Italy → 'LNEFRC90M22F205G' (Codice Fiscale)
generate('sg.id') // Singapore → 'S6763250H' (NRIC)
// A whole coherent record — any country:
generate('de.person')
// → {
// name: 'Mia Zimmermann', phone: '0179 1480356', id: 'L0PYL465F',
// tax: '74259823954', bank: 'HypoVereinsbank', swift: 'HYVEDEMMXXX',
// account: 'DE68700202706388873556', amount: '€16.680,00'
// }
generateMany('br.person', 500) // → 500 coherent Brazilian records (core fields)
// `person` returns the common fields; ask for everything with `full`
// (or an explicit list) — cards, passport + MRZ, transaction, IP, income…:
generate('de.full') // every field
generate('de.person', { fields: 'all' }) // same thing
generate('de.person', { fields: ['name', 'card', 'passport_mrz'] })
// Generic types (uuid, enum, int, date, vin, isbn…) — validated where it
// counts (VIN/ISBN/IMEI check digits, RFC-4122 UUIDs):
generate('any.uuid') // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
generate('any.enum:free,pro') // 'pro'
generate('any.vin') // valid VIN check digit
// Custom schema — mix generic types AND country-accurate fields, à la Mockaroo:
import { generateSchema } from '@sampuli/data'
generateSchema({
count: 100,
fields: [
{ name: 'id', type: 'uuid' },
{ name: 'phone', type: 'ke.phone' }, // country-accurate
{ name: 'plan', type: 'enum:free,pro,team' },
{ name: 'mrr', type: 'int:0,500', blank: 0.1 }, // 10% null
],
})
// Beyond people — companies and products, correct for the country:
generate('de.company') // → { name: 'Nova Labs GmbH', industry: 'Software',
// vat: 'DE114...', phone: '0179...', currency: 'EUR', … }
generate('jp.product') // → { name: 'Pro Speaker', price: 8250, currency: 'JPY',
// ean: '4988... ' (valid EAN-13), sku: 'KT-402917', … }
Every pack uses the same spec shape — swap the country code. A pack's fields
and presets vary by country; discover them with listFields(code) /
listPresets(code). Records stay internally coherent (bank SWIFT, gender
name, town region, email name).
CommonJS works too:
const { generate } = require('@sampuli/data')
What you can generate
generate(spec, settings?) — the spec is pack.selector:
| Spec | Returns |
|---|---|
'ke.phone', 'ke.kra_pin', 'ke.national_id', … |
a single field value |
'ke.person' (or just 'ke') |
a full coherent record (object) |
'ke.preset:kyc' |
ordered [label, value] rows for a scenario |
Discover what a pack offers:
import { listFields, listPresets } from '@sampuli/data'
listFields('ke') // [{ key: 'phone', label: 'Phone', num: false, ... }, ...]
listPresets('ke') // [{ key: 'kyc', name: 'KYC customer profile', ... }, ...]
Kenya's field keys, for example: name, phone, id, kra, bank,
swift, account, amount, gender, dob, marital, occupation,
town, county, gps, address, business, paybill, till, plate,
email, avatar, ref. Presets: kyc, pesalink, schoolfees, fx,
merchant. Other countries share the common core (name/phone/id/tax/bank/
swift/account/amount) with locale-specific extras and presets — always check
listFields(code).
Every country also has payment-card and passport fields (off by default):
card (Luhn-valid PAN, scheme-correct BIN), card_scheme (Visa / Mastercard /
American Express), card_expiry (MM/YY), card_cvv, passport (number),
passport_expiry, and passport_mrz — a full ICAO-9303 TD3 machine-readable
zone with correct check digits, coherent with the record's name and gender.
generate('dk.card') // → '4536 3094 5269 0134' (Visa, Luhn-valid)
generate('gb.person', { fields: ['name', 'card', 'card_scheme', 'card_cvv', 'passport'] })
generate('za.passport_mrz') // → two 44-char MRZ lines with valid ICAO check digits
And a set of cross-country extras (also off by default): a transaction
(txn_datetime, txn_type, txn_status, txn_channel, txn_mcc with real
ISO-18245 codes), ip / ipv6, licence (driver's licence), income
(currency-magnitude-aware for the pack's currency), employer, and country.
generate('jp.income') // → '9,970,000 JPY' (scaled to the local currency)
generate('ke.person', { fields: ['name', 'txn_datetime', 'txn_type', 'txn_mcc', 'ip', 'income'] })
Choosing which fields a record has
generate('ke.person') returns the on-by-default columns. To include the
optional PII — email, GPS, date of birth, address, occupation, avatar, plate,
and the rest — pass fields (the package equivalent of the app's column
toggles):
generate('ke.person', { fields: 'all' })
// → every field the pack has: name, phone, id, kra, …, gender, dob, gps,
// address, email, avatar, ref
generate('ke.person', { fields: ['name', 'phone', 'email', 'gps', 'dob'] })
// → { name, phone, email, gps, dob } — exactly those, in that order
generateMany('za.person', 500, { fields: ['name', 'said', 'phone', 'email'] })
Records stay coherent whichever fields you pick (e.g. email derives from the
generated name).
Settings
generate('ke.phone', { phoneFmt: 'plus' }) // '+254712345678'
generate('ke.phone', { network: 'safaricom' }) // Safaricom prefixes only
generate('ke.kra_pin', { pinType: 'P' }) // company PIN (P…)
generate('ke.amount', { amountStyle: 'number' }) // 12400 (number, not 'KES 12,400.00')
Records stay internally coherent: bank and swift describe the same bank,
gender matches the first name, county matches the town.
Reproducible output
Pass a seed and you get the same data every time — ideal for CI snapshots:
generate('ke.person', { seed: 'test-42' }) // identical on every run
generateMany('ke.person', 100, { seed: 'batch' }) // identical batch every run
Scope
Ships every Sampuli country pack, free, with no volume caps — countries across
Africa, Europe, the Middle East, Asia-Pacific and the Americas, and growing.
Call listPacks() for the full set installed, or browse them at
sampuli.site. Additional packs can be plugged in at
runtime via registerPack(pack).
Accuracy & synthetic data
Every value is format-correct: it matches the real shape and passes standard format checks. It is entirely synthetic — invented at call time and corresponding to no real person, account, business, or registered number. It is for testing only; do not present any output as a genuine KRA PIN, national ID, tax number, IBAN, or bank account.
License
MIT.