Java
The official Java client library for the InternetData API.
Getting Started
<dependency>
<groupId>io.internetdata</groupId>
<artifactId>internetdata</artifactId>
<version>2.1.0</version>
</dependency>implementation 'io.internetdata:internetdata:2.1.0'Requires Java 17 or newer. HTTP is the JDK's own java.net.http.HttpClient, so there is no third-party HTTP stack to reconcile with yours.
Usage
Every database endpoint is licensed, so you need an API key carrying the db.download scope. Create one in the console, then:
import io.internetdata.InternetData;
InternetData client = InternetData.create(System.getenv("INTERNETDATA_API_KEY"));Build the client once and keep it. It owns a connection pool, and it is thread safe.
The database calls hang off client.database(), which is where the sibling VPNDetection library keeps the same seven.
What you can see
import io.internetdata.model.Database;
for (Database family : client.database().list()) {
System.out.println(family.getBase() + " " + family.getStanding() + " " + family.getLicenseType());
family.getVersions().forEach(v -> System.out.println(" " + v.getId() + " " + v.getFormats()));
}A license covers a database family (bogon_ip), while a download names a version (bogon_ip_v1), so the ids every other method takes come from getVersions().
getStanding() is LICENSED for a live grant, EXPIRED for one whose term has ended, and UNLICENSED for a database published but never bought, which is how you discover what else exists.
What is inside one
import io.internetdata.model.DatabaseMetadata;
DatabaseMetadata meta = client.database().metadata("bogon_ip_v1");
meta.getUpdated(); // the date this build was generated
meta.getEntries(); // rows in it
meta.getSize(); // bytes, per format
meta.getSchema(); // columns, per format
meta.getSample(); // a few real rows, per formatPoll this to decide whether today's build is worth fetching, and to budget a transfer before you start one.
Downloading
import io.internetdata.DatabaseFormat;
import java.nio.file.Path;
// Streamed straight to disk, so nothing bigger than a chunk is ever held in memory.
long written = client.database().download("bogon_ip_v1", DatabaseFormat.CSVGZ, Path.of("bogon_ip_v1.csv.gz"));A transfer that dies half way is reported rather than left on disk: the bytes land in a neighboring .part file, what arrived is checked against the length the server promised, and only a whole file is moved into place.
The API answers a download with a redirect to a time-limited URL on object storage. If you would rather run the transfer yourself, ask for that link instead. It authorizes itself, so it carries none of your credentials and can be handed to any downloader:
String url = client.database().downloadUrl("bogon_ip_v1", DatabaseFormat.CSVGZ);The link authorizes the start of a transfer, so one already running is not interrupted when it lapses.
There is also an in-memory form, for the small end of the catalog:
byte[] raw = client.database().downloadBytes("bogon_asn_v1", DatabaseFormat.CSVGZ);downloadBytes holds the whole file in memory, and the catalog spans a few hundred bytes to several gigabytes, so check client.database().metadata(...).getSize() before reaching for it and use download for anything you have not measured.
Verifying a download
import io.internetdata.model.DatabaseChecksums;
DatabaseChecksums sums = client.database().checksums("bogon_ip_v1", DatabaseFormat.CSVGZ);
sums.getSha256();Download history
Your organization's recent attempts, newest first, refusals included, which is what answers "it stopped working":
client.database().downloads().forEach(d ->
System.out.println(d.getCreated() + " " + d.getDatasetId() + " " + d.getOutcome()));
client.database().downloads(10); // newest tenErrors
Failures throw an InternetDataException carrying a kind() and a retryable() flag. It is unchecked, so it travels through a stream or a future without being wrapped first:
import io.internetdata.InternetDataException;
try {
client.database().download("bogon_ip_v1", DatabaseFormat.MMDB, Path.of("bogon_ip_v1.mmdb"));
} catch (InternetDataException e) {
System.err.println(e.kind() + " " + e.getMessage() + " retryable=" + e.retryable());
}kind() is one of BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, RATE_LIMITED, QUOTA_EXCEEDED, SERVER_ERROR or NETWORK. getMessage() is the API's own result code where it sent one, so a refusal reads NOT_LICENSED or LICENSE_EXPIRED rather than "403".
Note that RATE_LIMITED and QUOTA_EXCEEDED both arrive as HTTP 429 and are not the same thing. A rate limit is the API protecting itself and retrying later works; a spent quota needs your allowance raised or the window to roll over. The library retries rate limits for you, and 5xx and transport failures, but never a spent quota or any other client error.
Sign in with OAuth (device flow)
A program running on the person's own machine can let them sign in with a browser and pick one of their API keys, instead of asking them to paste it:
InternetData client = InternetData.create();
DeviceAuthorization device = client.oauth().deviceAuthorization("your-client-id",
new DeviceAuthorizationOptions().scope("account.read apikeys.read apikeys.reveal"));
System.out.println("Open " + device.getVerificationUri() + " and enter " + device.getUserCode());
TokenResponse token = client.oauth().pollDeviceToken("your-client-id", device);
if (token.getApikey() == null) {
throw new IllegalStateException("no API key came back: none was picked, or it cannot be shown again");
}
InternetData keyed = InternetData.builder().apiKey(token.getApikey()).build();A denied sign-in throws OauthAccessDeniedException and a code that ran out OauthExpiredTokenException, and client IDs are issued on request from support@internetdata.io. client.oauth().revoke("your-client-id", token.getRefreshToken()) signs the machine out again.