API Documentation

Integrate PenipuMY scam data into your apps, bots, and workflows.

The PenipuMY API provides programmatic access to Malaysia's community-driven scam database. Query phone numbers, bank accounts, social media profiles, and URLs against verified scam reports.

Getting Access
Generate your API key in Account Settings. Free tier includes 100 requests/day.

Authentication

All requests require an API key sent via the X-API-Key header.

cURL
curl -H "X-API-Key: YOUR_API_KEY" https://penipu.my/api/v1/stats

Generate your API key in Account Settings.

Security
Keep your API key secret. Do not expose it in client-side code or public repositories.

Rate Limits

Pricing effective: 14 September 2026

TierLimitPrice
Free100 requests/dayFree
Starter10,000 requests/monthRM59/mo
Business50,000 requests/monthRM199/mo
Enterprise500,000 requests/monthRM999/mo
Bonus Quota+5000 requestsRM30 (one-time)

Bonus credits are used when your daily limit is exceeded and don't expire. Upgrade tiers or buy quota at Account Settings.

Free-tier limit resets at midnight MYT (UTC+8). Paid tiers reset monthly on your subscription's billing date (the day you subscribed), not a shared calendar date. Check response headers:

Response Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997

Search the scam database by phone number, bank account, social media, or general query.

GET /api/v1/search
Search scam profiles by phone, bank account, social media, or keyword.

Parameters

ParamTypeRequiredDescription
qstringRequiredSearch query (min 3 chars). Phone number, bank account, name, or social username.
typestringOptionalauto (default), phone, bank, social, name
limitintegerOptionalMax results. Default: 10, Max: 25

Example Request

cURL
curl -H "X-API-Key: YOUR_KEY" "https://penipu.my/api/v1/search?q=0123456789"
Python
import requests

resp = requests.get(
    "https://penipu.my/api/v1/search",
    headers={"X-API-Key": "YOUR_KEY"},
    params={"q": "0123456789", "type": "phone"}
)
print(resp.json())

Response

200 OK
{
  "query": "0123456789",
  "type": "phone",
  "count": 1,
  "results": [
    {
      "profile_id": "pid-abc123",
      "name": "Suspect Name",
      "total_reports": 5,
      "total_loss_myr": 25000.0,
      "asset_counts": {
        "bank_accounts": 2,
        "phone_numbers": 3,
        "social_accounts": 1
      },
      "last_updated": "2026-02-28 12:00:00"
    }
  ]
}

Profile Details

Get detailed information about a specific scam profile, including all linked reports.

GET /api/v1/profile/{profile_id}
Retrieve a scam profile with linked reports and platform data.

Example Request

cURL
curl -H "X-API-Key: YOUR_KEY" "https://penipu.my/api/v1/profile/pid-abc123"

Response

200 OK
{
  "profile_id": "pid-abc123",
  "name": "Suspect Name",
  "total_reports": 5,
  "total_loss_myr": 25000.0,
  "asset_counts": {
    "bank_accounts": 2,
    "phone_numbers": 3,
    "social_accounts": 1
  },
  "platforms_involved": ["Instagram", "TikTok"],
  "reports": [
    {
      "report_id": 42,
      "public_token": "aB3dEfGhIjKlMnOp",
      "url": "https://penipu.my/report/view/aB3dEfGhIjKlMnOp",
      "status": "VERIFIED",
      "amount_lost_myr": 5000.0,
      "submitted_at": "2026-01-15 09:30:00"
    }
  ],
  "created_at": "2025-06-01 10:00:00",
  "last_updated": "2026-02-28 12:00:00"
}

Use url to link directly to a report — it uses public_token automatically when available. Linking off a raw report_id may stop working if report-link enumeration protection is enabled.

Phone Lookup

Quick lookup for a phone number. Returns police report count, community verified reports, and spam/fraud flags.

GET /api/v1/phone?q={phone_number}

Parameters

ParamRequiredDescription
qRequiredPhone number (8-15 digits). Supports +60, 60, or 0 prefix.

Example

cURL
curl -H "X-API-Key: YOUR_KEY" "https://penipu.my/api/v1/phone?q=0123456789"

Response

JSON
{
  "phone": "0320260239",
  "police_report_count": 0,
  "police_report_status": "live",
  "verified_report_count": 0,
  "spam": false,
  "fraud": false,
  "business": {
    "tier": "verified",
    "brand_slug": "maybank",
    "brand_name": "Maybank",
    "branch_slug": "klcc",
    "branch_name": "KLCC",
    "display_name": "Maybank - KLCC",
    "is_ambiguous": false,
    "logo_url": "https://penipu.my/uploads/logo_maybank_8bb5199e.png",
    "website": "https://www.maybank2u.com.my",
    "place_url": "https://www.google.com/maps/place/...",
    "address": "Lot G45, Suria KLCC, Kuala Lumpur",
    "rating": 4.2,
    "review_count": 186,
    "opening_hours_status": "Open · Closes 12 am",
    "wheelchair_accessible": true,
    "scam_alert_banner": null
  }
}
FieldTypeDescription
phonestringNormalized phone number
police_report_countintegerNumber of PDRM police reports (via SemakMule)
police_report_statusstringSource of police report count: live (fresh from SemakMule), cached (within 24h), cached_stale (older, SemakMule unreachable), unavailable (no cache and SemakMule down)
verified_report_countintegerNumber of community verified reports
spambooleanFlagged as spam by caller ID sources
fraudbooleanFlagged as fraud (verified reports or caller ID)
businessobject | nullBusiness directory match. Present when the phone belongs to a verified business (bank branch, merchant, etc.) or a compromised business number. null for unknown numbers.

Business Object Fields

Returned when the phone matches a record in the business directory.

FieldTypeDescription
business.tierstringTrust tier: verified (legitimate business, admin-verified), verified_spoofing (verified business with ≥3 police reports — likely being spoofed by scammers), compromised (business number reported in active fraud), free (unverified directory listing).
business.brand_slugstringURL-safe brand identifier (e.g. maybank, cimb).
business.brand_namestringBrand display name.
business.branch_slugstring | nullURL-safe branch identifier. null for brand-level numbers (e.g. customer hotlines).
business.branch_namestring | nullBranch display name (e.g. KLCC, Customer Hotline).
business.display_namestringPre-formatted "Brand - Branch" string for UI.
business.is_ambiguousbooleantrue when the same phone serves multiple branches (e.g. customer service routing).
business.logo_urlstring | nullFull HTTPS URL to brand logo image.
business.websitestring | nullBranch-specific or brand primary website URL.
business.place_urlstring | nullGoogle Maps place URL for the branch.
business.addressstring | nullBranch postal address.
business.ratingnumber | nullGoogle Maps rating (0.0-5.0).
business.review_countinteger | nullGoogle Maps review count.
business.opening_hours_statusstring | nullLive-computed status (e.g. "Open · Closes 4 pm", "Closed · Opens 9:30 am", "Open 24 hours"). Computed in Malaysia time.
business.wheelchair_accessibleboolean | nullAccessibility flag from Maps. null when unknown.
business.scam_alert_bannerstring | nullActive scam advisory banner text from admins (e.g. BNM alerts). null when none.
business.spoofing_report_countintegerOnly present when tier == "verified_spoofing". Number of police reports against this verified business number — strong signal of impersonation/spoofing.

Bank Account Lookup

Quick lookup for a bank account number. Returns police report count, community verified reports, and fraud flag.

GET /api/v1/bank?q={account_number}

Parameters

ParamRequiredDescription
qRequiredBank account number (6-24 digits). Hyphens and spaces stripped automatically.

Example

cURL
curl -H "X-API-Key: YOUR_KEY" "https://penipu.my/api/v1/bank?q=1234567890"

Response

JSON
{
  "bank_account": "1234567890",
  "holder_name": "AHMAD BIN ALI",
  "bank_name": "Maybank Berhad",
  "police_report_count": 2,
  "police_report_status": "live",
  "verified_report_count": 5,
  "fraud": true
}
FieldTypeDescription
bank_accountstringNormalized account number (digits only)
holder_namestring | nullAccount holder name from reports (null if not on file)
bank_namestring | nullBank name from reports (null if not on file)
police_report_countintegerNumber of PDRM police reports (via SemakMule)
police_report_statusstringSource of police report count: live (fresh from SemakMule), cached (within 24h), cached_stale (older, SemakMule unreachable), unavailable (no cache and SemakMule down)
verified_report_countintegerNumber of community verified reports
fraudbooleanTrue if verified reports exist

Platform Statistics

Get platform-wide statistics including total reports, profiles, and losses tracked.

GET /api/v1/stats
Platform-wide scam statistics.

Example Request

cURL
curl -H "X-API-Key: YOUR_KEY" "https://penipu.my/api/v1/stats"

Response

200 OK
{
  "total_profiles": 1234,
  "total_reports": 5678,
  "verified_reports": 3456,
  "total_loss_myr": 2500000.0,
  "total_bank_accounts_tracked": 890,
  "total_phone_numbers_tracked": 1200,
  "total_social_accounts_tracked": 450
}

Error Handling

The API uses standard HTTP status codes and returns JSON error responses.

StatusMeaning
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid API key
404Not found — profile does not exist
429Rate limit exceeded — wait until midnight MYT
500Server error

All errors return JSON:

Error Response
{
  "error": "Query parameter "q" is required (min 3 characters)."
}

PenipuMY API v1 — penipu.my