
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
@sampuli/data
Advanced tools
Synthetic, format-true test data for countries worldwide — national IDs, tax numbers, IBANs & SWIFT/BIC, bank accounts, phone numbers and ready-made KYC/transfer/payment scenarios, each correct for its own country. For QA and CI at banks and fintechs anyw
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
| 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 (bank↔SWIFT, gender↔name) | 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.
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.
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
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')
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'] })
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).
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.
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
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).
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.
MIT.
FAQs
Synthetic, format-true test data for countries worldwide — national IDs, tax numbers, IBANs & SWIFT/BIC, bank accounts, phone numbers and ready-made KYC/transfer/payment scenarios, each correct for its own country. For QA and CI at banks and fintechs anyw
The npm package @sampuli/data receives a total of 17 weekly downloads. As such, @sampuli/data popularity was classified as not popular.
We found that @sampuli/data demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.