A simple ruby wrapper for Have I Been Pwned v3 API.
It simplifies the interaction with all the endpoints included in version 3 of their API.
Install the gem and add to the application's Gemfile by executing:
bundle add 'have_i_been_pwned_api'
If bundler is not being used to manage dependencies, install the gem by executing:
gem install 'have_i_been_pwned_api'
First configure the API client on an initializer, you can either use a default setup or configure your own. You can configure the follwing optional parameters of the gem:
Parameter | Required | Type | Description |
---|---|---|---|
api_key |
False |
String |
Required only if using other than PwnedPassword endpoints |
user_agent |
False |
String |
User agent used on requests to API. Required by HIBP. Defaults to "have_i_been_pwned_api gem [current gem version]" |
# If you only want access to pwdned passwords endpoint and default config
HaveIBeenPwnedApi.configure
# If you want to specify your api key and user agent
HaveIBeenPwnedApi.configure do |config|
config.api_key = ENV['HIBP_API_KEY']
config.user_agent = "your_custom_user_agent"
end
The usage of this endpoint does not require a HIBP key configured, but it can be used also if you have one.
This API endpoint returns the total times a given password has been pwned. It returns 0
whenever it has not been pwned.
It protects the value of queried passwords by using k-Anonymity model, which allows a password to be searched for by using only a partial hash. You can read more about k-anonimity here
Parameter | Required | Type | Description |
---|---|---|---|
password |
True |
String |
(e.g. "password" ) Represents the password searched for |
HaveIBeenPwnedApi::PwnedPasswords.check_pwd(password: "your-password")
# => 1724
# Returns 0 if the password has not been compromised
# otherwise returns the total amount of times it has been compromised
The usage of the following endpoints requires a HIBP api key configured.
This API returns the most recently added breach based on the "AddedDate"
attribute of the breach model. This may not be the most recent breach to occur as there may be significant lead time between a service being breached and the data later appearing on HIBP.
latest_breach = HaveIBeenPwnedApi::Breaches.latest_breach
# Returns an instance of HaveIBeenPwnedApi::Models::Breach
latest_breach.class
=> HaveIBeenPwnedApi::Models::Breach
latest_breach.name
=> "SamsungGermany"
See more about the Breach model and how to access its values in Models section
This API endpoint returns the details of each of the breaches in the system stored in a BreachCollection
# Returns an instance of HaveIBeenPwnedApi::Models::BreachCollection
collection = HaveIBeenPwnedApi::Breaches.breaches
collection.breaches.class
# => HaveIBeenPwnedApi::Models::BreachCollection
collection.breaches.count
# => 882
See more about BreachCollection model and how to access its values in Models section
You can pass optional keyword arguments to narrow down the returned set:
Parameter | Required | Type | Description |
---|---|---|---|
domain |
False |
String |
(e.g. "adobe.com" ) Filters the result set to only breaches against the domain specified. It is possible that one site (and consequently domain), is compromised on multiple occasions. |
is_spam_list |
False |
Boolean |
Filters the result set to only breaches that either are or are not flagged as a spam list. |
# Fetch only breaches against adobe.com that are flagged as spam list
collection = HaveIBeenPwnedApi::Breaches.breaches(domain: "adobe.com", is_spam_list: true)
This API endpoint returns a list of all breaches a particular account has been involved in. The API requires a single parameter which is the account to be searched for.
Parameter | Required | Type | Description |
---|---|---|---|
account |
True |
String |
(e.g. "[email protected]" ) Not case-sensitive. Represents the account searched for |
truncate_response |
False |
Boolean |
Default true . By default reduces the size of responses by 98% returning a collection of TruncatedBreach. If set to false it will return a collection of full Breach objects |
domain |
False |
string |
e.g. "adobe.com" ) Filters the result set to only breaches against the domain specified. It is possible that one site (and consequently domain), is compromised on multiple occasions. |
include_unverified |
False |
Boolean |
Returns breaches that have been flagged as "unverified". By default, both verified and unverified breaches are returned when performing a search |
# By default returns a BreachCollection of TruncatedBreaches
collection = HaveIBeenPwnedApi::Breaches.breached_account(account: "[email protected]")
# => #<HaveIBeenPwnedApi::Models::BreachCollection
collection.breaches.first.class
# => HaveIBeenPwnedApi::Models::TruncatedBreach
If no breach is found an empty BreachCollection is returned
<HaveIBeenPwnedApi::Models::BreachCollection @breaches=[]>
If you set truncate_response
to false
, you will get a collection of full breach models.
collection = HaveIBeenPwnedApi::Breaches.breached_account(account: "[email protected]",
truncate_response: false,
domain: "example.com", include_unverified: false))
# => #<HaveIBeenPwnedApi::Models::BreachCollection
collection.breaches.first.class
# => HaveIBeenPwnedApi::Models::Breach
This API returns email addresses on a given domain and the breaches they've appeared in. Only domains that have been successfully added to your domain search dashboard will be returned.
# Returns a HaveIBeenPwnedApi::Models::BreachedDomain object
breached_domain = HaveIBeenPwnedApi::Breaches.breached_domain(domain: "mydomain.com")
# => HaveIBeenPwnedApi::Models::BreachedDomain
breached_domain.entries.count
# => 2
If it does not find a breach for the given domain, an empty BreachedDomain object is returned
<HaveIBeenPwnedApi::Models::BreachedDomain @entries={}>
See more about the BreachedDomain model and how to access its values in Models section
This API returns the full list of breached data classes as an ordered array of strings. Each string represents an attribute of records compromised on a breach. For example "Email addresses"
and "Passwords"
HaveIBeenPwnedApi::Breaches.data_classes
# =>
# ["Account balances",
# "Address book contacts",
# "Age groups",
# ...]
Domains that have been successfully added to your domain search dashboard are returned via this API.
# Returns an array of Domain objects
domains = HaveIBeenPwnedApi::Breaches.subscribed_domains
# =>
# [#<HaveIBeenPwnedApi::Models::Domain,
# ...]
See more about the Domain model and how to access its values in Models section
Returns all the Pastes
where a given account is present, stored on a PasteCollection
object. Takes a single parameter which is the email address to be searched for.
Parameter | Required | Type | Description |
---|---|---|---|
account |
True |
String |
(e.g. "[email protected]" ) Not case-sensitive. Represents the account searched for |
paste_collection = HaveIBeenPwnedApi::Pastes.paste_account(account: "[email protected]")
=>
#<HaveIBeenPwnedApi::Models::PasteCollection
See more about the PasteCollection model and how to access its values in Models section
All stealer log APIs require a Pwned 5 subscription or higher, regardless of domain size. Each search can only be performed against domains that have been successfully added to your domain search dashboard.
This API returns an array of domains where the given email and password where captured by an infostealer.
Parameter | Required | Type | Description |
---|---|---|---|
email |
True |
String |
(e.g. "[email protected]" ) Not case-sensitive. Represents the account searched for |
domains = HaveIBeenPwnedApi::StealerLogs.by_email(email: '[email protected]')
# => ["netflix.com", "spotify.com"]
This API returns an array of emails that have been captured by an infostealer on the given domain.
Parameter | Required | Type | Description |
---|---|---|---|
domain |
True |
String |
(e.g. "mydomain.com" ) Represents the domain searched for |
emails = HaveIBeenPwnedApi::StealerLogs.by_website_domain(domain: 'fiestup.com')
# => ["[email protected]", "[email protected]"]
This API returns stealer log data by the domain of the email address.
Parameter | Required | Type | Description |
---|---|---|---|
domain |
True |
String |
(e.g. "mydomain.com" ) Represents the domain searched for |
emails = HaveIBeenPwnedApi::StealerLogs.by_email_domain(domain: 'fiestup.com')
# => {"andy"=>["netflix.com"], "jane"=>["netflix.com", "spotify.com"]}
This API returns details of the current subscription
HaveIBeenPwnedApi::Subscription.status
# =>
# #<HaveIBeenPwnedApi::Models::SubscriptionStatus
# @description="Domains with up to 25 breached addresses each, and a rate limited API key allowing 10 email address searches per minute",
# @domain_search_max_breached_accounts=25,
# @rpm=10,
# @subscribed_until=#<DateTime: 2025-05-18T11:52:59+00:00 ((2460814j,42779s,0n),+0s,2299161j)>,
# @subscription_name="Pwned 1">
See more about SubsctiptionStatus model and how to access its values in Models section
A wrapper around an array of Breach
or TruncatedBreach
objects. Includes Enumerable
so you can iterate, filter, and query like a standard Ruby collection.
breaches
(Array<HaveIBeenPwnedApi::Models::Breach
orTruncatedBreach>
): the list of breach models in this collection
Represents the full details of a single breach returned by the Have I Been Pwned API.
<HaveIBeenPwnedApi::Models::Breach
@added_date=#<DateTime: 2025-04-13T00:24:36+00:00 ((2460779j,1476s,0n),+0s,2299161j)>,
@breach_date=#<Date: 2025-03-30 ((2460765j,0s,0n),+0s,2299161j)>,
@data_classes=["Email addresses", "Names", "Physical addresses", "Purchases", "Salutations", "Shipment tracking numbers", "Support tickets"],
@description=
"In March 2025, <a href=\"https://www.infostealers.com/article/samsung-tickets-data-leak-infostealers-strike-again-in-massive-free-dump/\" target=\"_blank\" rel=\"noopener\">data from Samsung Germany was compromised in a data breach of their logistics provider, Spectos</a>. Allegedly due to credentials being obtained by malware running on a Spectos employee's machine, the breach included 216k unique email addresses along with names, physical addresses, items purchased from Samsung Germany and related support tickets and shipping tracking numbers.",
@domain="samsung.de",
@is_fabricated=false,
@is_malware=false,
@is_retired=false,
@is_sensitive=false,
@is_spam_list=false,
@is_stealer_log=false,
@is_subscription_free=false,
@is_verified=true,
@logo_path="https://haveibeenpwned.com/Content/Images/PwnedLogos/Samsung.png",
@modified_date=#<DateTime: 2025-04-13T12:42:28+00:00 ((2460779j,45748s,0n),+0s,2299161j)>,
@name="SamsungGermany",
@pwn_count=216333,
@title="Samsung Germany Customer Tickets">
name
(String
): Internal, Pascal-cased unique breach identifier.title
(String
): User-friendly breach title .domain
(String
): Primary website domain where the breach occurred.breach_date
(Date
): Date the breach happened.added_date
(DateTime
): When the breach was added to HIBP.modified_date
(DateTime
): Last time the breach record was updated.pwn_count
(Integer
): Number of accounts loaded into the system for this breach.description
(String
): HTML overview of the incident (may include links, formatting).data_classes
(Array<String>
): List of data types compromised (e.g."Email addresses"
,"Passwords"
, etc.).- Boolean flags (
true
/false
):is_verified
is_fabricated
is_sensitive
is_retired
is_spam_list
is_malware
is_subscription_free
is_stealer_log
Indicate special breach characteristics (verified, fabricated, sensitive, etc.).
logo_path
(String
): URL to the breach’s PNG logo.
A simplified Breach model used when only breach names are returned (e.g. truncated responses).
name
(String
): Pascal-cased breach identifier (same as theName
field in the API).
Represents the result of a domain-scoped breach lookup, where each email alias is mapped to the list of breach names in which it appears.
<HaveIBeenPwnedApi::Models::BreachedDomain @entries={"alias1"=>["Adobe"], "alias2"=>["Adobe", "Gawker", "Stratfor"], "alias3"=>["AshleyMadison"]}>
entries
(Hash<String, Array<String>>
):
A hash mapping each email local-part (e.g."alias1"
for[email protected]
) to an array of Pascal-cased breach names:
{
"alias1" => ["Adobe"],
"alias2" => ["Adobe", "Gawker", "Stratfor"],
"alias3" => ["AshleyMadison"]
}
domain_name
(String
): The full domain name that has been successfully verified.pwn_count
(Integer
ornil
): Total breached email addresses found on the domain at last search (null if no searches yet).pwn_count_excluding_spam_lists
(Integer
ornil
): Same aspwn_count
, excluding breaches flagged as spam lists (null if no searches yet).pwn_count_excluding_spam_lists_at_last_subscription_renewal
(Integer
ornil
):pwn_count_excluding_spam_lists
value locked in when the current subscription began (null if never subscribed).next_subscription_renewal
(DateTime
ornil
): ISO 8601 timestamp when the current subscription ends (null if never subscribed).
A wrapper around an array of Paste
objects returned by the paste-related endpoints. Includes Enumerable
so you can iterate, filter, and query just like a standard Ruby collection.
<HaveIBeenPwnedApi::Models::PasteCollection
@pastes=
[#<HaveIBeenPwnedApi::Models::Paste @date=#<DateTime: 2016-06-22T11:06:26+00:00 ((2457562j,39986s,0n),+0s,2299161j)>, @domain=nil, @email_count=323, @id="Y8k3SJjg", @source="Pastebin", @title=nil>,
#<HaveIBeenPwnedApi::Models::Paste @date=#<DateTime: 2016-02-18T15:51:55+00:00 ((2457437j,57115s,0n),+0s,2299161j)>, @domain=nil, @email_count=2225, @id="X1tzUFdD", @source="Pastebin", @title=nil>, ...
pastes
(Array<HaveIBeenPwnedApi::Models::Paste>
):
The underlying list ofPaste
instances.
Represents a single paste record.
<HaveIBeenPwnedApi::Models::Paste @date=#<DateTime: 2014-03-04T19:14:54+00:00 ((2456721j,69294s,0n),+0s,2299161j)>, @domain=nil, @email_count=139, @id="8Q0BvKD8", @source="Pastebin", @title="syslog"
source
(String
): The service the paste came from (e.g."Pastebin"
,"Ghostbin"
,"JustPaste"
, etc.).id
(String
): The identifier assigned by the source service (used, together withsource
, to build the paste URL).title
(String
ornil
): The paste’s title as shown on the source site (may benil
if none was provided).date
(DateTime
ornil
): Timestamp when the paste was posted (precision to the second; may benil
if unavailable).email_count
(Integer
): Number of email addresses extracted from the paste.
Encapsulates your current subscription details and rate limits.
<HaveIBeenPwnedApi::Models::SubscriptionStatus
@description="Domains with up to 25 breached addresses each, and a rate limited API key allowing 10 email address searches per minute",
@domain_search_max_breached_accounts=25,
@rpm=10,
@subscribed_until=#<DateTime: 2025-05-18T11:52:59+00:00 ((2460814j,42779s,0n),+0s,2299161j)>,
@subscription_name="Pwned 1">
-
description
(String
):
Human-readable summary of your subscription tier (e.g."Domains with up to 25 breached addresses each, and a rate limited API key allowing 10 email address searches per minute"
). -
domain_search_max_breached_accounts
(Integer
):
Maximum number of breached accounts returned per domain search (e.g.25
). -
rpm
(Integer
):
Allowed email-search requests per minute (rate limit) (e.g.10
). -
subscribed_until
(DateTime
):
ISO 8601 timestamp when your current subscription expires (e.g.2025-05-18T11:52:59+00:00
). -
subscription_name
(String
):
The name of your subscription plan (e.g."Pwned 1"
).
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/hugo0706/have_i_been_pwned_api
The gem is available as open source under the terms of the MIT License.