---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/segment/connections/sources/catalog/libraries/server/java#article
headline: Analytics for Java
description: Use Segment's Analytics for Java library to record analytics data from your Java code and route it to any destination you enable.
url: https://www.twilio.com/docs/segment/connections/sources/catalog/libraries/server/java
inLanguage: en
dateModified: 2026-07-06T19:14:45.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Analytics for Java

**Support:**

* Community ✕
* Maintenance ✕
* Flagship ✓

Flagship libraries offer the most up-to-date functionality on Segment's most popular platforms. Segment actively maintains flagship libraries, which benefit from new feature releases and ongoing development and support.

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.segment.analytics.java/analytics/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.segment.analytics.java/analytics)

Segment's Analytics for Java library lets you record analytics data from your Java code. The requests hit Segment's servers, which then route your data to any analytics service you enable on your destinations page.

This library is open-source on [GitHub](https://github.com/segmentio/analytics-java).

All of Segment's server-side libraries are built for high performance, so you can use them in your web server controller code. This library uses an internal queue to make all calls non-blocking and fast. It also batches messages and flushes asynchronously to Segment's servers.

To stay updated on releases, subscribe to the [release feed](https://github.com/segmentio/analytics-java/releases.atom).

## Getting started

The recommended way to install the library for Java is with a build system like Gradle or Maven. This makes it simple to upgrade and swap out destinations. The library is distributed using [Maven Central](http://maven.org/)  as a `jar` dependency.

Here's what it would look like with Maven:

Add to `pom.xml`:

```xml
<dependency>
  <groupId>com.segment.analytics.java</groupId>
  <artifactId>analytics</artifactId>
  <version>LATEST</version>
</dependency>
```

or if you're using Gradle:

```bash
implementation 'com.segment.analytics.java:analytics:+'
```

### Initialize the SDK

Before you can send events to Segment, you need to initialize an instance of the Analytics class. To do so, you must use the `Analytics.Builder` class.

```java
Analytics analytics = Analytics.builder(writeKey).build();
```

Of course, you'll want to replace writeKey with your actual **Write Key** which you can find in Segment under your source settings.

The Builder can also be used to customize behavior of the Analytics instance.

**Note**: There is an internal `AnalyticsClient` class. Do not confuse this class with the public `Analytics` class and do not use this class directly.

The Analytics class has a method called `enqueue` that takes a `MessageBuilder`. Each message class has a corresponding builder that is used to construct instances of a message.

Although not enforced at compile time, make sure you provide either of `userId` or `anonymousId` for each message. Failing to do so will raise an exception at runtime.

The following examples use [Guava's](https://github.com/google/guava) immutable maps, but feel free to use plain old Java maps instead.

### Regional configuration

For Business plans with access to [Regional Segment](/docs/segment/guides/regional-segment), you can use the `host` configuration parameter to send data to the desired region:

1. Oregon (Default) — `api.segment.io/`
2. Dublin — `events.eu1.segmentapis.com/`

## Basic tracking methods

The basic tracking methods below serve as the building blocks of your Segment tracking. They include [Identify](#identify), [Track](#track), [Page](#page), [Group](#group), and [Alias](#alias). These methods correspond with those used in the [Segment Spec](/docs/segment/connections/spec/). The documentation on this page explains how to use these methods in Analytics for Java.

> \[!NOTE]
>
> For any of the different methods described on this page, you can replace the properties and traits in the code samples with variables that represent the data collected.
>
> Note that sending a property or trait with a null value won't be possible as Guava's immutable maps will reject the null value and the GSON library used to serialize the Java object will ignore it. As a workaround, you can send an empty string instead of a null value in on your properties or traits.

### Identify

Identify lets you tie a user to their actions and record traits about them. It includes a unique User ID and any optional traits you know about them.

Segment recommends calling Identify a single time when the user's account is first created, and only identifying again later when their traits change.

Example Identify call:

```java
Map<String, String> map = new HashMap();
map.put("name", "Michael Bolton");
map.put("email", "mbolton@example.com");

analytics.enqueue(IdentifyMessage.builder()
        .userId("f4ca124298")
        .traits(map));
```

This call identifies Michael by his unique User ID (the one you know him by in your database) and labeling him with `name` and `email` traits.

The Identify call has the following fields:

| `userId` *String*           | The ID for this user in your database.                                                     |
| --------------------------- | ------------------------------------------------------------------------------------------ |
| `traits` *Traits, optional* | A dictionary of traits you know about the user. Things like: `email`, `name` or `friends`. |

**Note**: The enqueue method takes a `MessageBuilder` instance and not a `Message` instance directly. This is to allow you to use a `MessageTransformer` that applies to all incoming messages and transform or add data. {/* LR: can't find this section, commenting out. Read more in our [transformer reference section](/docs/connections/sources/catalog/libraries/server/java#transformer).*/}

Find details on the **identify method payload** in the [Segment Spec](/docs/segment/connections/spec/identify/).

### Track

Track lets you record the actions your users perform. Every action triggers what Segment calls an "event", which can also have associated properties.

You'll want to track events that you're interested in, such as **Signed Up**, **Item Purchased** or **Article Bookmarked**.

To get started, Segment recommends tracking just a few important events. You can always add more later.

Example Track call:

```java
analytics.enqueue(TrackMessage.builder("Item Purchased")
    .userId("f4ca124298")
    .properties(ImmutableMap.builder()
        .put("revenue", 39.95)
        .put("shipping", "2-day")
        .build()
    )
);
```

This example Track call tells you that your user just triggered the **Item Purchased** event with a revenue of $39.95 and chose your hypothetical '2-day' shipping.

Track event properties can be anything you want to record. In this case, revenue and shipping.

The Track call has the following fields:

| `userId` *String*                   | The ID for this user in your database.                                                                                            |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `event` *String*                    | The name of the event you're tracking. Segment recommends human-readable names like **Song Played** or **Status Updated**.        |
| `properties` *Properties, optional* | A dictionary of properties for the event. If the event was **Product Added**, it might have properties like `price` or `product`. |

Find details on **best practices in event naming** as well as the **Track method payload** in the [Segment Spec](/docs/segment/connections/spec/track/).

### Screen

The [Screen](/docs/segment/connections/spec/screen/) method lets you record whenever a user sees a screen of your mobile app, along with optional extra information about the screen being viewed.

You'll want to record a screen event an event whenever the user opens a screen in your app. This could be a view, fragment, dialog or activity depending on your app.

Not all services support screen, so when it's not supported explicitly, the screen method tracks as an event with the same parameters.

Example Screen call:

```java
analytics.enqueue(ScreenMessage.builder("Schedule")
    .userId("f4ca124298")
    .properties(ImmutableMap.builder()
        .put("category", "Sports")
        .put("path", "/sports/schedule")
        .build()
    )
);
```

The Screen call has the following fields:

| `userId` *String*                   | The ID for this user in your database.                                                                                         |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `name` *String*                     | The webpage name you're tracking. Segment recommends human-readable names like **Login** or **Register**.                      |
| `properties` *Properties, optional* | A dictionary of properties for the screen visit. If the screen was **Login**, it might have properties like `path` or `title`. |

Find details on the **Screen payload** in the [Segment Spec](/docs/segment/connections/spec/screen/).

### Page

The [Page](/docs/segment/connections/spec/page/) method lets you record whenever a user sees a page of your website, along with optional extra information about the page being viewed.

Not all services support page, so when it's not supported explicitly, the Page method typically tracks as an event with the same parameters.

Example Page call:

```java
analytics.enqueue(PageMessage.builder("Schedule")
    .userId("f4ca124298")
    .properties(ImmutableMap.builder()
        .put("category", "Sports")
        .put("path", "/sports/schedule")
        .build()
    )
);
```

The Page call has the following fields:

| `userId` *String*                   | The ID for this user in your database.                                                                                     |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `name` *String*                     | The webpage name you're tracking. Segment recommends human-readable names like **Login** or **Register**.                  |
| `properties` *Properties, optional* | A dictionary of properties for the page visit. If the page was **Login**, it might have properties like `path` or `title`. |

Find details on the **Page payload** in the [Segment Spec](/docs/segment/connections/spec/page/).

### Group

Group lets you associate an [identified user](/docs/segment/connections/sources/catalog/libraries/server/java/#identify) user with a group. A group could be a company, organization, account, project or team. It also lets you record custom traits about the group, like industry or number of employees.

This is useful for tools like [Intercom](/docs/segment/connections/destinations/catalog/intercom/), [Preact](/docs/segment/connections/destinations/catalog/preact/) and [Totango](/docs/segment/connections/destinations/catalog/totango/), as it ties the user to a **group** of other users.

Example Group call:

```java
analytics.enqueue(GroupMessage.builder("some-group-id")
    .userId("f4ca124298")
    .traits(ImmutableMap.builder()
        .put("name", "Segment")
        .put("size", 50)
        .build()
    )
);
```

The Group call has the following fields:

| `userId` *String*           | The ID for this user in your database.                                             |
| --------------------------- | ---------------------------------------------------------------------------------- |
| `groupId` *String*          | The ID for this group in your database.                                            |
| `traits` *Traits, optional* | A dictionary of traits you know about the group. Things like: `name` or `website`. |

Find more details about Group, including the **Group payload**, in the [Segment Spec](/docs/segment/connections/spec/group/).

### Alias

Alias is how you associate one identity with another. This is an advanced method, but it is required to manage user identities successfully in *some* of our destinations.

In [Mixpanel](/docs/segment/connections/destinations/catalog/mixpanel/#alias) it's used to associate an anonymous user with an identified user once they sign up. For [Kissmetrics](/docs/segment/connections/destinations/catalog/kissmetrics/#alias), if your user switches IDs, you can use 'alias' to rename the 'userId'.

Example Alias call:

```java
analytics.enqueue(AliasMessage.builder("previousId")
    .userId("f4ca124298")
);
```

Here's a full example of how you might use the Alias call:

```java
// the anonymous user does actions ...
track("anonymous_user", "Anonymous Event");
// the anonymous user signs up and is aliased
alias("anonymous_user", "identified@example.com");
// the signed up user is identified
identify("identified@example.com", new Traits("plan", "Free"));
// the identified user does actions ...
track("identified@example.com", "Identified Action");
```

For more details about Alias, including the **Alias call payload**, check out the [Segment Spec](/docs/segment/connections/spec/alias/).

## Historical import

You can import historical data by adding the `timestamp` argument to any of your method calls. This can be helpful if you've just switched to Segment.

Historical imports can only be done into destinations that can accept historical timestamped data. Most analytics tools like Mixpanel, Amplitude, and Kissmetrics can handle that type of data just fine. One common destination that does not accept historical data is Google Analytics since their API cannot accept historical data.

**Note**: If you're tracking things that are happening right now, leave out the `timestamp` and Segment's servers will timestamp the requests for you.

```java
Date historicalDate = ...;
analytics.enqueue(TrackMessage.builder("Button Clicked")
    .userId("f4ca124298")
    .timestamp(historicalDate)
);
```

## Selecting destinations

The Alias, Group, Identify, Page, and Track calls can all be passed an object of `integrations` that lets you turn certain destinations on or off. By default all destinations are enabled.

Similar to timestamp, the builders take a map of destinations that control which analytics destinations you want each message to go to.

```java
analytics.enqueue(TrackMessage.builder("Button Clicked")
    .userId("f4ca124298")
    .enableIntegration("All", false)
    .enableIntegration("Amplitude", true)
);
```

In this case, you're specifying that you want this identify to only go to Amplitude. `"All", false` says that no destination should be enabled unless otherwise specified, and `{ "Amplitude", true }` turns on Amplitude.

Destination flags are **case sensitive** and match [the destination's name in the docs](/docs/segment/connections/destinations/) (for example, "AdLearn Open Platform", "awe.sm", or "MailChimp").

**Note**:

* Business Tier users can filter Track calls right from the Segment UI on your source schema page. Segment recommends using the UI if possible since it's a much simpler way of managing your filters and can be updated with no code changes on your side.
* If you are on a grandfathered plan, events sent server-side that are filtered through the Segment dashboard still count towards your API usage.

## Context

If you're running a web server, you might want to send context variables such as `userAgent` or `ip` with your `page` or `screen` calls. You can do so by setting the `Context`.

```java
analytics.enqueue(TrackMessage.builder("Button Clicked")
    .userId("f4ca124298")
    .context(ImmutableMap.builder()
        .put("ip", "12.212.12.49")
        .put("language", "en-us")
        .build()
    )
);
```

## Batching

Segment's libraries are built to support high performance environments. That means it is safe to use analytics-java on a web server that's serving hundreds of requests per second. For more information, check out the [java benchmark](https://github.com/segmentio/analytics-java-benchmark).

Every method you call **does not** result in an HTTP request, but is queued in memory instead. Messages are flushed in batch in the background, which allows for much faster operation.

There is a maximum of `500KB` per batch request and `32KB` per call.

> \[!WARNING]
>
> Segment's HTTP Tracking API accepts batch requests up to **500 KB**. To avoid errors in event creation, ensure that individual event payload sizes remain below **32 KB**.

## How do I flush on demand?

You can also flush on demand. For example, at the end of your program, you'll want to flush to make sure there's nothing left in the queue. Just call the `flush` method:

```java
analytics.flush()
```

Calling this method notifies the client to upload any events in the queue. If you need a blocking flush implementation, see the [`BlockingFlush` example on GitHub](https://github.com/segmentio/analytics-java/blob/master/analytics-sample/src/main/java/sample/BlockingFlush.java).

## How do I gzip requests?

The Java library does not automatically gzip requests, but allows you to do so if you desire using interceptors in [OkHttp](https://github.com/square/okhttp/wiki/Interceptors#rewriting-requests). See the [sample app](https://github.com/segmentio/analytics-java/blob/master/analytics-sample/src/main/java/sample/Main.java) in Segment's `analytics-java` repository for a working example.

## Multiple clients

Different parts of your app may require different types of batching. In that case, you can initialize different `Analytics` instances. Simply use the builder method (you can reuse it with different parameters) to create different instances.

```java
Analytics.Builder builder = Analytics.builder(writeKey);
Analytics first = builder.build();
Analytics second = builder.flushInterval(2, TimeUnit.SECONDS).build();
```

## Logging

You can enable verbose logging to see what data is being sent over HTTP when debugging issues. You can enable logging by initializing the library like this:

```java
Log STDOUT = new Log() {
    @Override
    public void print(Level level, String format, Object... args) {
        System.out.println(level + ":\t" + String.format(format, args));
    }

    @Override
    public void print(Level level, Throwable error, String format, Object... args) {
        System.out.println(level + ":\t" + String.format(format, args));
        System.out.println(error);
    }
};

Analytics analytics = Analytics.builder("<writeKey>")
        .log(STDOUT)
        .build();
```

For more advance logging, you can check out the [sample code](https://github.com/segmentio/analytics-java/tree/master/analytics-sample/src/main/java/sample) in Segment's open-source library.

## Options

Use `Analytics.Builder` to customize the behavior of your Analytics instance. Each method on the builder returns the builder itself, allowing you to chain options together:

```java
Analytics analytics = Analytics.builder("<writeKey>")
    .flushQueueSize(100)
    .flushInterval(30, TimeUnit.SECONDS)
    .retries(3)
    .callback(myCallback)
    .build();
```

| Option                                    | Description                                                                                                                                                               |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client(OkHttpClient)`                    | Set a custom OkHttp networking client. Useful for configuring timeouts, proxies, or other HTTP settings.                                                                  |
| `log(Log)`                                | Configure a logging implementation. By default, nothing is logged. See [Logging](#logging) for an example.                                                                |
| `endpoint(String)`                        | Set the host to upload events to. Default: `https://api.segment.io`. Appends `/v1/import/` to the provided host.                                                          |
| `setUploadURL(String)`                    | Set the full upload URL (host and path). Use instead of `endpoint` when you need control over the full URL. Default: `https://api.segment.io/v1/import/`.                 |
| `flushQueueSize(int)`                     | Number of messages to accumulate in the queue before triggering a flush. Default: `250`.                                                                                  |
| `flushInterval(long, TimeUnit)`           | How often to flush the queue, regardless of how many messages are in it. Minimum is 1 second. Default: `10 seconds`.                                                      |
| `maximumQueueSizeInBytes(int)`            | Maximum size of the in-memory queue in bytes before a flush is triggered. Default: `500KB`.                                                                               |
| `queueCapacity(int)`                      | Maximum number of messages allowed in the queue at any time. If the limit is reached, new messages are dropped until the queue drains. Default: `Integer.MAX_VALUE`.      |
| `retries(int)`                            | Maximum number of upload attempts before a batch is discarded. Default: `10`.                                                                                             |
| `maxTotalBackoffDuration(long, TimeUnit)` | Maximum total time spent retrying a batch with exponential backoff before giving up. Default: `12 hours`.                                                                 |
| `maxRateLimitDuration(long, TimeUnit)`    | Maximum total time spent retrying a batch after receiving a `429 Too Many Requests` response before giving up. Default: `12 hours`.                                       |
| `callback(Callback)`                      | Add a `Callback` to be notified when a message is successfully uploaded or when an upload fails. Multiple callbacks can be registered.                                    |
| `networkExecutor(ExecutorService)`        | Set the `ExecutorService` used to make HTTP requests. By default, a single-threaded executor is used.                                                                     |
| `threadFactory(ThreadFactory)`            | Set the `ThreadFactory` used to create background threads.                                                                                                                |
| `messageTransformer(MessageTransformer)`  | Add a `MessageTransformer` that can mutate or drop messages before they are enqueued. Multiple transformers run in registration order.                                    |
| `messageInterceptor(MessageInterceptor)`  | Add a `MessageInterceptor` that can mutate or drop built `Message` objects before they are sent. Multiple interceptors run in registration order.                         |
| `plugin(Plugin)`                          | Apply a `Plugin` that configures the builder. Useful for packaging a set of options as a reusable component.                                                              |
| `gsonBuilder(GsonBuilder)`                | Provide a custom `GsonBuilder` for JSON serialization.                                                                                                                    |
| `forceTlsVersion1()`                      | Force the client to include TLS 1.0 and 1.1 in addition to TLS 1.2/1.3 in the connection spec. Only needed for legacy server environments that do not support modern TLS. |

## Non-blocking sends with `offer`

The default `enqueue` method blocks the calling thread when the internal message queue is full. If you're working in a latency-sensitive path (for example, an HTTP request handler), use `offer` instead. It attempts to add the message to the queue and returns immediately with a boolean result:

```java
boolean accepted = analytics.offer(TrackMessage.builder("Item Purchased")
    .userId("f4ca124298")
    .properties(ImmutableMap.builder()
        .put("revenue", 39.95)
        .put("shipping", "2-day")
        .build()
    ));

if (!accepted) {
  // Queue is full — retry later or send to a dead letter queue
  deadLetterQueue.add(trackMessage);
}
```

`offer` returns `false` when:

* The queue has reached its `queueCapacity` limit.
* The message exceeds the per-message size limit (32 KB).
* The Analytics instance has been shut down.

Because a rejected message never enters the internal queue, the `callback` you register with the builder is not invoked for it. Handle rejected messages at the call site — for example, by scheduling a retry or routing them to the same dead letter queue you use in your `Callback.failure` implementation.

Use `offer` when dropping an analytics event is preferable to adding latency or risking thread starvation. Use `enqueue` when delivery of every event is critical and you can tolerate back-pressure.

## Callbacks

Register a `Callback` to be notified when messages are delivered or permanently fail. Callback methods are invoked on background threads, so implementations should be non-blocking and handle their own synchronization if needed.

```java
Analytics analytics = Analytics.builder("<writeKey>")
    .callback(new Callback() {
      @Override
      public void success(Message message) {
        log.info("Delivered: {}", message.messageId());
      }

      @Override
      public void failure(Message message, Throwable throwable) {
        // Retries exhausted — route to a dead letter queue or alert
        deadLetterQueue.add(message);
        log.error("Failed to deliver: {}", message.messageId(), throwable);
      }
    })
    .build();
```

`success` is called once a batch containing the message is accepted by Segment's API. `failure` is called when all retry attempts are exhausted or an unrecoverable error occurs. You can register multiple callbacks — they all fire for every message.

## Java support

Segment supports Java 8, 9, 10, and 11. The library may work on other versions of Java as well, however Segment doesn't test for compatibility on unsupported versions.

## Snapshots

To add a snapshot dependency to your builds, make sure you add the snapshot repository so your build system can look up the dependency.

Maven users can add the following to their `pom.xml`:

```text
<repository>
    <id>ossrh</id>
    <name>Sonatype Snapshot Repository</name>
    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    <releases>
        <enabled>false</enabled>
    </releases>
    <snapshots>
        <enabled>true</enabled>
    </snapshots>
</repository>
```

Gradle users should declare this in their repositories block:

```text
repositories {
  mavenCentral()
  maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
```

## Troubleshooting

The following tips often help resolve common issues.

### No events in my debugger

1. Double check that you've followed all the steps in the [Quickstart](quickstart/).
2. Make sure that you're calling a Segment API method once the library is successfully installed—[`identify`](#identify), [`track`](#track), etc.
3. Make sure your application isn't shutting down before the `Analytics.Client` local queue events are pushed to Segment. You can manually call `Analytics.Client.Flush()` to ensure the queue is fully processed before shutdown.

### Other common errors

If you are experiencing data loss from your source, you may be experiencing one or more of the following common errors:

* **Payload is too large**: If you attempt to send events larger than 32KB per normal API request or batches of events larger than 500KB per request, Segment's tracking API responds with `400 Bad Request`. Try sending smaller events (or smaller batches) to correct this error.
* **Identifier is not present**: Segment's tracking API requires that each payload has a `userId` and/or `anonymousId`. If you send events without either the `userId` or `anonymousId`, Segment's tracking API responds with an `no_user_anon_id` error. Check the event payload and client instrumentation for more details.
* **Track event is missing name**: All Track events to Segment must have a name in string format.
* **Event dropped during deduplication**: Segment automatically adds a `messageId` field to all payloads and uses this value to deduplicate events. If you're manually setting a `messageId` value, ensure that each event has a unique value.
* **Incorrect credentials**: Double check your credentials for your downstream destination(s).
* **Destination incompatibility**: Make sure that the destination you are troubleshooting can accept server-side API calls. You can see compatibility information on the [Destination comparison by category](/docs/segment/connections/destinations/category-compare/) page and in the documentation for your specific destination.
* **Destination-specific requirements**: Check out the [destination's documentation](/docs/segment/connections/destinations/) to see if there are other requirements for using the method and destination that you're trying to get working.
