diff --git a/CHANGELOG.md b/CHANGELOG.md
index 378a2fc65..c698e824f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG
+## v8.7.0 (2026-02-25)
+
+- Adds generic `makeApiCall` function
+
## v8.6.0 (2026-02-03)
- Adds the following functions usable by child and referral customer users:
diff --git a/README.md b/README.md
index 98a5ceefb..1a3006999 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Add this to your project's POM:
com.easypost
easypost-api-client
- 8.6.0
+ 8.7.0
```
@@ -25,7 +25,7 @@ Add this to your project's POM:
Add this to your project's build file:
```groovy
-implementation "com.easypost:easypost-api-client:8.6.0"
+implementation "com.easypost:easypost-api-client:8.7.0"
```
**NOTE:** [Google Gson](http://code.google.com/p/google-gson/) is required.
diff --git a/VERSION b/VERSION
index acd405b1d..df5119ec6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.6.0
+8.7.0
diff --git a/pom.xml b/pom.xml
index 643345c3b..23bd125d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.easypost
easypost-api-client
- 8.6.0
+ 8.7.0
jar
com.easypost:easypost-api-client
diff --git a/src/main/java/com/easypost/service/EasyPostClient.java b/src/main/java/com/easypost/service/EasyPostClient.java
index 8b21287f9..2e6b72b9f 100644
--- a/src/main/java/com/easypost/service/EasyPostClient.java
+++ b/src/main/java/com/easypost/service/EasyPostClient.java
@@ -1,13 +1,17 @@
package com.easypost.service;
+import java.util.Map;
import java.util.function.Function;
import com.easypost.Constants;
+import com.easypost.exception.EasyPostException;
import com.easypost.exception.General.MissingParameterError;
import com.easypost.hooks.RequestHook;
import com.easypost.hooks.RequestHookResponses;
import com.easypost.hooks.ResponseHook;
import com.easypost.hooks.ResponseHookResponses;
+import com.easypost.http.Requestor;
+import com.easypost.http.Requestor.RequestMethod;
import lombok.Getter;
@@ -215,4 +219,23 @@ public String getApiVersion() {
public String getApiBase() {
return apiBase;
}
+
+ /**
+ * Make an API call to the EasyPost API.
+ *
+ * This public, generic interface is useful for making arbitrary API calls to the EasyPost API that
+ * are not yet supported by the client library's services. When possible, the service for your use case
+ * should be used instead as it provides a more convenient and higher-level interface depending on the endpoint.
+ *
+ * @param method The HTTP method to use for the request.
+ * @param endpoint The endpoint to call (e.g., "/addresses").
+ * @param params The parameters to send with the request.
+ * @return A Map containing the API response.
+ * @throws EasyPostException when the request fails.
+ */
+ @SuppressWarnings("unchecked")
+ public Map makeApiCall(RequestMethod method, String endpoint, Map params)
+ throws EasyPostException {
+ return Requestor.request(method, endpoint, params, Map.class, this);
+ }
}
diff --git a/src/test/cassettes/client/make_api_call.json b/src/test/cassettes/client/make_api_call.json
new file mode 100644
index 000000000..29ee12a76
--- /dev/null
+++ b/src/test/cassettes/client/make_api_call.json
@@ -0,0 +1,88 @@
+[
+ {
+ "recordedAt": 1770917472,
+ "request": {
+ "body": "",
+ "method": "GET",
+ "headers": {
+ "Accept-Charset": [
+ "UTF-8"
+ ],
+ "User-Agent": [
+ "REDACTED"
+ ]
+ },
+ "uri": "https://api.easypost.com/v2/addresses?%70%61%67%65%5F%73%69%7A%65\u003d%31"
+ },
+ "response": {
+ "body": "{\n \"addresses\": [\n {\n \"zip\": \"00001\",\n \"country\": \"US\",\n \"city\": \"NOT A CITY\",\n \"created_at\": \"2025-11-07T18:29:36Z\",\n \"verifications\": {\n \"delivery\": {\n \"success\": true,\n \"details\": {\n \"latitude\": null,\n \"time_zone\": null,\n \"longitude\": null\n },\n \"errors\": [\n {\n \"code\": \"E.ADDRESS.NOT_FOUND\",\n \"field\": \"address\",\n \"suggestion\": null,\n \"message\": \"Address not found\"\n }\n ]\n },\n \"verify_carrier\": \"ups\",\n \"zip4\": {\n \"success\": true,\n \"details\": null,\n \"errors\": [\n {\n \"code\": \"E.ADDRESS.NOT_FOUND\",\n \"field\": \"address\",\n \"suggestion\": null,\n \"message\": \"Address not found\"\n }\n ]\n }\n },\n \"mode\": \"test\",\n \"federal_tax_id\": null,\n \"state_tax_id\": null,\n \"carrier_facility\": null,\n \"residential\": null,\n \"updated_at\": \"2025-11-07T18:29:36Z\",\n \"phone\": \"REDACTED\",\n \"name\": null,\n \"company\": \"EASYPOST\",\n \"street1\": \"000 UNKNOWN STREET\",\n \"id\": \"adr_b6c1cfd0bc0711f0ac45ac1f6bc539ae\",\n \"street2\": null,\n \"state\": \"ZZ\",\n \"email\": \"TEST@EXAMPLE.COM\",\n \"object\": \"Address\"\n }\n ],\n \"has_more\": true\n}",
+ "httpVersion": null,
+ "headers": {
+ "null": [
+ "HTTP/1.1 200 OK"
+ ],
+ "content-length": [
+ "842"
+ ],
+ "expires": [
+ "0"
+ ],
+ "x-node": [
+ "bigweb53nuq"
+ ],
+ "x-frame-options": [
+ "SAMEORIGIN"
+ ],
+ "x-download-options": [
+ "noopen"
+ ],
+ "x-permitted-cross-domain-policies": [
+ "none"
+ ],
+ "x-backend": [
+ "easypost"
+ ],
+ "pragma": [
+ "no-cache"
+ ],
+ "strict-transport-security": [
+ "max-age\u003d31536000; includeSubDomains; preload"
+ ],
+ "x-xss-protection": [
+ "1; mode\u003dblock"
+ ],
+ "x-content-type-options": [
+ "nosniff"
+ ],
+ "x-ep-request-uuid": [
+ "f785caef698e0e60e787ccbd0122bf10"
+ ],
+ "x-proxied": [
+ "intlb5nuq 0dcc3a6efb",
+ "extlb2nuq c01291cd8f"
+ ],
+ "referrer-policy": [
+ "strict-origin-when-cross-origin"
+ ],
+ "x-runtime": [
+ "0.039820"
+ ],
+ "content-type": [
+ "application/json; charset\u003dutf-8"
+ ],
+ "x-version-label": [
+ "easypost-202602121702-bfe72e7ac7-master"
+ ],
+ "cache-control": [
+ "private, no-cache, no-store"
+ ]
+ },
+ "status": {
+ "code": 200,
+ "message": "OK"
+ },
+ "uri": "https://api.easypost.com/v2/addresses?%70%61%67%65%5F%73%69%7A%65\u003d%31"
+ },
+ "duration": 238
+ }
+]
\ No newline at end of file
diff --git a/src/test/java/com/easypost/EasyPostClientTest.java b/src/test/java/com/easypost/EasyPostClientTest.java
new file mode 100644
index 000000000..369fa1332
--- /dev/null
+++ b/src/test/java/com/easypost/EasyPostClientTest.java
@@ -0,0 +1,47 @@
+package com.easypost;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import com.easypost.exception.EasyPostException;
+import com.easypost.http.Requestor.RequestMethod;
+
+public final class EasyPostClientTest {
+ private static TestUtils.VCR vcr;
+
+ /**
+ * Set up the testing environment for this file.
+ *
+ * @throws EasyPostException when the request fails.
+ */
+ @BeforeAll
+ public static void setup() throws EasyPostException {
+ vcr = new TestUtils.VCR("client", TestUtils.ApiKey.TEST);
+ }
+
+ /**
+ * Test making a generic API call.
+ *
+ * @throws EasyPostException when the request fails.
+ */
+ @Test
+ public void testMakeApiCall() throws EasyPostException {
+ vcr.setUpTest("make_api_call");
+
+ Map params = new HashMap<>();
+ params.put("page_size", 1);
+
+ Map response = vcr.client.makeApiCall(RequestMethod.GET, "addresses", params);
+
+ List> addresses = (List>) response.get("addresses");
+ assertEquals(1, addresses.size());
+ Map firstAddress = (Map) addresses.get(0);
+ assertEquals("Address", firstAddress.get("object"));
+ }
+}