> ## Documentation Index
> Fetch the complete documentation index at: https://newscatcherinc-docs.mintlify.site/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Java SDK

> Java client library for CatchAll API

Java SDK provides access to the CatchAll API from Java applications.

## Installation

<Tabs>
  <Tab title="Gradle">
    ```groovy theme={null}
    dependencies {
        implementation 'com.newscatcherapi:newscatcher-catchall-sdk:3.0.0'
    }
    ```
  </Tab>

  <Tab title="Maven">
    ```xml theme={null}
    <dependency>
        <groupId>com.newscatcherapi</groupId>
        <artifactId>newscatcher-catchall-sdk</artifactId>
        <version>3.0.0</version>
    </dependency>
    ```
  </Tab>
</Tabs>

## Quickstart

Get started with CatchAll in three steps:

<Steps>
  <Step title="Initialize the client">
    ```java theme={null}
    import com.newscatcher.catchall.CatchAllApi;

    CatchAllApi client = CatchAllApi.builder()
        .apiKey("YOUR_API_KEY")
        .build();
    ```
  </Step>

  <Step title="Create a job">
    ```java theme={null}
    import com.newscatcher.catchall.resources.jobs.requests.SubmitRequestDto;

    var job = client.jobs().createJob(
        SubmitRequestDto.builder()
            .query("AI company acquisitions")
            .limit(10)
            .build()
    );
    String jobId = job.getJobId();
    ```
  </Step>

  <Step title="Wait and retrieve results">
    ```java theme={null}
    import com.newscatcher.catchall.types.StatusResponseDto;
    import com.newscatcher.catchall.types.PublicJobStatus;

    final int POLL_INTERVAL_MS = 60000;

    // Poll for completion
    while (true) {
        StatusResponseDto status = client.jobs().getJobStatus(jobId);
        if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
            break;
        }
        Thread.sleep(POLL_INTERVAL_MS);
    }

    // Get results
    var results = client.jobs().getJobResults(jobId);
    System.out.println("Found " + results.getValidRecords().orElse(0) + " valid records");
    ```
  </Step>
</Steps>

<Note>
  Jobs process asynchronously and typically complete in 10-15 minutes. See the
  [Quickstart](/web-search-api/get-started/quickstart) for a complete walkthrough.
</Note>

## Working with jobs

<Tabs>
  <Tab title="Get suggestions">
    Preview suggested validators, enrichments, and date ranges before creating a job:

    ```java theme={null}
    import com.newscatcher.catchall.CatchAllApiClient;
    import com.newscatcher.catchall.resources.jobs.requests.InitializeRequestDto;
    import com.newscatcher.catchall.types.InitializeResponseDto;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;

    CatchAllApiClient client = CatchAllApiClient.builder()
        .apiKey("YOUR_API_KEY")
        .build();

    InitializeResponseDto suggestions = client.jobs().initialize(
        InitializeRequestDto.builder()
            .query("AI company acquisitions")
            .context("Focus on deal size and acquiring company details")
            .build()
    );

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.INDENT_OUTPUT);
    System.out.println(mapper.writeValueAsString(suggestions));
    ```

    <Expandable title="suggestions response">
      ```json theme={null}
      {
        "validators": [
          {
            "name": "is_acquisition_event",
            "description": "true if article describes a completed or announced acquisition",
            "type": "boolean"
          },
          {
            "name": "involves_ai_company",
            "description": "true if acquiring or acquired company is in AI sector",
            "type": "boolean"
          }
        ],
        "enrichments": [
          {
            "name": "acquirer_company",
            "description": "Extract the acquiring company name",
            "type": "company"
          },
          {
            "name": "acquired_company",
            "description": "Extract the acquired company name",
            "type": "company"
          },
          {
            "name": "deal_value",
            "description": "Extract acquisition price if mentioned",
            "type": "number"
          },
          {
            "name": "announcement_date",
            "description": "Extract date of announcement",
            "type": "date"
          },
          {
            "name": "acquirer_details",
            "description": "Extract details about the acquiring company",
            "type": "text"
          }
        ],
        "start_date": "2026-02-01T14:12:57.292205+00:00",
        "end_date": "2026-02-06T14:12:57.292205+00:00",
        "date_modification_message": [
          "No dates were provided; using a default window of 5 days (2026-02-01 to 2026-02-06)."
        ]
      }
      ```
    </Expandable>

    <Tip>
      To learn more, see the [Initialize endpoint](/web-search-api/api-reference/jobs/initialize-job).
    </Tip>
  </Tab>

  <Tab title="Create and track">
    Submit a query and track its progress:

    ```java theme={null}
    import com.newscatcher.catchall.resources.jobs.requests.SubmitRequestDto;
    import com.newscatcher.catchall.types.StatusResponseDto;
    import com.newscatcher.catchall.types.PublicJobStatus;

    final int POLL_INTERVAL_MS = 60000;

    // Create job with custom validators and enrichments
    var job = client.jobs().createJob(    
        SubmitRequestDto.builder()
            .query("AI company acquisitions")
            .context("Focus on deal size and acquiring company details")
            .limit(10)
            .validators(
                ValidatorSchema.builder()
                    .name("is_acquisition_event")
                    .description("true if article describes a completed or announced acquisition")
                    .type("boolean")
                    .build()
            )
            .enrichments(
                EnrichmentSchema.builder()
                    .name("acquirer_company")
                    .description("Extract the acquiring company name")
                    .type(EnrichmentType.COMPANY)
                    .build(),
                EnrichmentSchema.builder()
                    .name("acquired_company")
                    .description("Extract the acquired company name")
                    .type(EnrichmentType.COMPANY)
                    .build(),
                EnrichmentSchema.builder()
                    .name("deal_value")
                    .description("Extract acquisition price if mentioned")
                    .type(EnrichmentType.NUMBER)
                    .build()
            )
            .build()
    );
    System.out.println("Job created: " + job.getJobId());

    // Monitor progress
    String jobId = job.getJobId();
    while (true) {
        StatusResponseDto status = client.jobs().getJobStatus(jobId);

        if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
            break;
        }

        status.getSteps().stream()
            .filter(step -> !step.getCompleted().orElse(false))
            .findFirst()
            .ifPresent(step -> System.out.println(String.format(
                "Step %d/7: %s",
                step.getOrder(),
                step.getStatus()
            )));

        Thread.sleep(POLL_INTERVAL_MS);
    }

    // Retrieve results
    var results = client.jobs().getJobResults(jobId);
    System.out.println("\nFound " + results.getValidRecords().orElse(0) + " valid records");
    results.getAllRecords().ifPresent(records ->
        records.forEach(record -> System.out.println("  " + record.getRecordTitle()))
    );
    ```

    <Note>
      Validators and enrichments are optional. If not provided, the system
      generates them automatically based on your query.
    </Note>
  </Tab>

  <Tab title="Continue jobs">
    Extend processing limits for completed jobs:

    ```java theme={null}
    import com.newscatcher.catchall.resources.jobs.requests.ContinueRequestDto;
    import com.newscatcher.catchall.types.ContinueResponseDto;

    final int POLL_INTERVAL_MS = 60000;

    // Continue job to process more records
    ContinueResponseDto continued = client.jobs().continueJob(
        ContinueRequestDto.builder()
            .jobId(jobId)
            .newLimit(50)
            .build()
    );
    System.out.println(String.format(
        "Continued: %s -> %s records",
        continued.getPreviousLimit(),
        continued.getNewLimit()
    ));

    // Wait for completion
    while (true) {
        StatusResponseDto status = client.jobs().getJobStatus(jobId);
        if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
            break;
        }
        Thread.sleep(POLL_INTERVAL_MS);
    }

    // Get final results
    var finalResults = client.jobs().getJobResults(jobId);
    System.out.println("Total: " + finalResults.getValidRecords().orElse(0) + " valid records");
    ```

    <Tip>
      Use `limit` parameter when creating jobs to start with fewer records for quick testing.
      Continue the job if you need more records after reviewing initial results.
    </Tip>
  </Tab>

  <Tab title="Early results">
    Retrieve partial results during the enriching stage:

    ```java theme={null}
    import com.newscatcher.catchall.types.PullJobResponseDto;

    final int POLL_INTERVAL_MS = 60000;

    while (true) {
        StatusResponseDto status = client.jobs().getJobStatus(jobId);

        if (PublicJobStatus.ENRICHING.equals(status.getStatus().orElse(null)) || 
            PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
            
            PullJobResponseDto results = client.jobs().getJobResults(jobId);
            
            if (results.getValidRecords().orElse(0) > 0) {
                System.out.println(String.format(
                    "Progress: %d/%d validated, %d valid",
                    results.getProgressValidated().orElse(0),
                    results.getCandidateRecords().orElse(0),
                    results.getValidRecords().orElse(0)
                ));
            }

            if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
                break;
            }
        }

        Thread.sleep(POLL_INTERVAL_MS);
    }
    ```
  </Tab>

  <Tab title="List jobs">
    Retrieve all jobs created by your account:

    ```java theme={null}
    import com.newscatcher.catchall.types.ListUserJobsResponseDto;
    import java.util.List;

    List<ListUserJobsResponseDto> jobs = client.jobs().getUserJobs();

    jobs.forEach(job -> {
        System.out.println(String.format(
            "Job %s: %s (%s)",
            job.getJobId(),
            job.getQuery(),
            job.getStatus()
        ));
    });
    ```
  </Tab>
</Tabs>

<Accordion title="Complete example with all features">
  ```java theme={null}
  import com.newscatcher.catchall.CatchAllApi;
  import com.newscatcher.catchall.resources.jobs.requests.SubmitRequestDto;
  import com.newscatcher.catchall.resources.jobs.requests.ContinueRequestDto;
  import com.newscatcher.catchall.types.StatusResponseDto;
  import com.newscatcher.catchall.types.PullJobResponseDto;
  import com.newscatcher.catchall.types.PublicJobStatus;
  import com.newscatcher.catchall.core.NewscatcherApiApiException;

  public class CompleteExample {
      public static void main(String[] args) {
          final int POLL_INTERVAL_MS = 60000;

          CatchAllApi client = CatchAllApi.builder()
              .apiKey("YOUR_API_KEY")
              .timeout(30)
              .maxRetries(3)
              .build();

          try {
              // Create job with custom enrichments
              var job = client.jobs().createJob(
                  SubmitRequestDto.builder()
                      .query("AI company acquisitions")
                      .context("Focus on deal size and acquiring company details")
                      .limit(10)
                      .enrichments(
                          EnrichmentSchema.builder()
                              .name("acquirer_company")
                              .description("Extract the acquiring company name")
                              .type(EnrichmentType.COMPANY)
                              .build(),
                          EnrichmentSchema.builder()
                              .name("deal_value")
                              .description("Extract acquisition price if mentioned")
                              .type(EnrichmentType.NUMBER)
                              .build()
                      )
                      .build()
              );
              String jobId = job.getJobId();
              System.out.println("Job created: " + jobId);

              // Poll with early results access
              while (true) {
                  StatusResponseDto status = client.jobs().getJobStatus(jobId);

                  if (PublicJobStatus.ENRICHING.equals(status.getStatus().orElse(null)) || 
                      PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
                      
                      PullJobResponseDto results = client.jobs().getJobResults(jobId);
                      if (results.getValidRecords().orElse(0) > 0) {
                          System.out.println(String.format(
                              "Progress: %d valid records",
                              results.getValidRecords().orElse(0)
                          ));
                      }

                      if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
                          break;
                      }
                  }

                  Thread.sleep(POLL_INTERVAL_MS);
              }

              // Continue if needed
              PullJobResponseDto results = client.jobs().getJobResults(jobId);
              if (results.getValidRecords().orElse(0) >= 10) {
                  client.jobs().continueJob(
                      ContinueRequestDto.builder()
                          .jobId(jobId)
                          .newLimit(50)
                          .build()
                  );

                  while (true) {
                      StatusResponseDto status = client.jobs().getJobStatus(jobId);
                      if (PublicJobStatus.COMPLETED.equals(status.getStatus().orElse(null))) {
                          break;
                      }
                      Thread.sleep(POLL_INTERVAL_MS);
                  }

                  results = client.jobs().getJobResults(jobId);
              }

              // Display results
              System.out.println("\nFinal: " + results.getValidRecords().orElse(0) + " valid records");
              results.getAllRecords().ifPresent(records ->
                  records.forEach(record -> System.out.println("  " + record.getRecordTitle()))
              );

          } catch (NewscatcherApiApiException e) {
              System.err.println("Status: " + e.statusCode());
              System.err.println("Error: " + e.body());
          } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
              System.err.println("Operation interrupted");
          }
      }
  }
  ```
</Accordion>

## Working with monitors

Automate recurring queries with scheduled execution.

<Tabs>
  <Tab title="Create monitor">
    Create a monitor from a completed job:

    ```java theme={null}
    import com.newscatcher.catchall.resources.monitors.requests.CreateMonitorRequestDto;
    import com.newscatcher.catchall.types.CreateMonitorResponseDto;
    import com.newscatcher.catchall.types.WebhookDto;
    import com.newscatcher.catchall.types.WebhookDtoMethod;
    import java.util.Map;

    CreateMonitorResponseDto monitor = client.monitors().createMonitor(
        CreateMonitorRequestDto.builder()
            .referenceJobId(jobId)
            .schedule("every day at 12 PM UTC")
            .webhookIds(List.of("a1b2c3d4-e5f6-7890-abcd-ef1234567890"))
            .build()
    );
    System.out.println("Monitor created: " + monitor.getMonitorId().orElse("N/A"));
    ```

    <Note>
      Monitors require a minimum 24-hour interval between executions. Learn more in the
      [Monitors documentation](/web-search-api/concepts/monitors).
    </Note>
  </Tab>

  <Tab title="Update monitor">
    Update webhook configuration for an existing monitor:

    ```java theme={null}
    import com.newscatcher.catchall.resources.monitors.requests.UpdateMonitorRequestDto;
    import com.newscatcher.catchall.types.UpdateMonitorResponseDto;

    String monitorId = monitor.getMonitorId().orElseThrow();

    UpdateMonitorResponseDto updated = client.monitors().updateMonitor(
        monitorId,
        UpdateMonitorRequestDto.builder()
            .webhookIds(List.of("b2c3d4e5-f6a7-8901-bcde-f12345678901"))
            .limit(100)
            .build()
    );
    System.out.println("Monitor updated: " + updated.getStatus());
    ```
  </Tab>

  <Tab title="Pause/Resume">
    Control monitor execution:

    ```java theme={null}
    String monitorId = monitor.getMonitorId().orElseThrow();

    // Pause monitor
    client.monitors().disableMonitor(monitorId);
    System.out.println("Monitor paused");

    // Resume monitor
    client.monitors().enableMonitor(monitorId);
    System.out.println("Monitor resumed");
    ```
  </Tab>

  <Tab title="List monitors">
    Retrieve all monitors for your account:

    ```java theme={null}
    import com.newscatcher.catchall.types.ListMonitorsResponseDto;

    ListMonitorsResponseDto monitors = client.monitors().listMonitors();

    System.out.println("Total monitors: " + monitors.getTotalMonitors());
    monitors.getMonitors().forEach(m -> {
        String status = m.getEnabled() ? "active" : "paused";
        System.out.println(String.format("%s: %s", m.getMonitorId(), status));
    });
    ```
  </Tab>

  <Tab title="Retrieve results">
    Access aggregated results from all monitor executions:

    ```java theme={null}
    import com.newscatcher.catchall.resources.monitors.requests.ListMonitorJobsRequest;
    import com.newscatcher.catchall.resources.monitors.types.ListMonitorJobsResponse;
    import com.newscatcher.catchall.types.PullMonitorResponseDto;

    String monitorId = monitor.getMonitorId().orElseThrow();

    // List execution history
    ListMonitorJobsResponse jobs = client.monitors().listMonitorJobs(
        monitorId,
        ListMonitorJobsRequest.builder()
            .sort("desc")
            .build()
    );
    System.out.println("Monitor executed " + jobs.getTotalJobs() + " jobs");

    // Get all collected records
    PullMonitorResponseDto results = client.monitors().pullMonitorResults(monitorId);
    System.out.println("Total records: " + results.getRecords().orElse(0));

    results.getAllRecords().ifPresent(records ->
        records.forEach(record -> {
            System.out.println("  " + record.getRecordTitle());
            System.out.println("  Added: " + record.getAddedOn());
        })
    );
    ```
  </Tab>
</Tabs>

<Accordion title="Complete monitor example">
  ```java theme={null}
  import com.newscatcher.catchall.CatchAllApi;
  import com.newscatcher.catchall.resources.monitors.requests.CreateMonitorRequestDto;
  import com.newscatcher.catchall.resources.monitors.requests.UpdateMonitorRequestDto;
  import com.newscatcher.catchall.resources.monitors.requests.ListMonitorJobsRequest;
  import com.newscatcher.catchall.resources.monitors.types.ListMonitorJobsResponse;
  import com.newscatcher.catchall.types.CreateMonitorResponseDto;
  import com.newscatcher.catchall.types.UpdateMonitorResponseDto;
  import com.newscatcher.catchall.types.ListMonitorsResponseDto;
  import com.newscatcher.catchall.types.PullMonitorResponseDto;
  import com.newscatcher.catchall.types.WebhookDto;
  import com.newscatcher.catchall.types.WebhookDtoMethod;
  import com.newscatcher.catchall.core.NewscatcherApiApiException;
  import java.util.Map;

  public class MonitorExample {
      public static void main(String[] args) {
          CatchAllApi client = CatchAllApi.builder()
              .apiKey("YOUR_API_KEY")
              .build();

          try {
              // Create monitor from completed job
              String jobId = "af7a26d6-cf0b-458c-a6ed-4b6318c74da3";

              CreateMonitorResponseDto monitor = client.monitors().createMonitor(
                  CreateMonitorRequestDto.builder()
                      .referenceJobId(jobId)
                      .schedule("every day at 12 PM UTC")
                      .webhookIds(List.of("a1b2c3d4-e5f6-7890-abcd-ef1234567890"))
                      .build()
              );
              String monitorId = monitor.getMonitorId().orElseThrow();
              System.out.println("Monitor created: " + monitorId);

              // Update webhook
              client.monitors().updateMonitor(
                  monitorId,
                  UpdateMonitorRequestDto.builder()
                      .webhookIds(List.of("b2c3d4e5-f6a7-8901-bcde-f12345678901"))
                      .limit(100)
                      .build()
              );

              // List all monitors
              ListMonitorsResponseDto allMonitors = client.monitors().listMonitors();
              allMonitors.getMonitors().forEach(m -> {
                  String status = m.getEnabled() ? "active" : "paused";
                  System.out.println(String.format("%s: %s", m.getMonitorId(), status));
              });

              // Control execution
              client.monitors().disableMonitor(monitorId);
              client.monitors().enableMonitor(monitorId);

              // List execution history
              ListMonitorJobsResponse jobs = client.monitors().listMonitorJobs(
                  monitorId,
                  ListMonitorJobsRequest.builder().sort("desc").build()
              );
              System.out.println("\nMonitor executed " + jobs.getTotalJobs() + " jobs");
              jobs.getJobs().forEach(job -> {
                  System.out.println(String.format(
                      "  Job %s: %s to %s",
                      job.getJobId(),
                      job.getStartDate(),
                      job.getEndDate()
                  ));
              });

              // Get aggregated results
              PullMonitorResponseDto results = client.monitors().pullMonitorResults(monitorId);
              System.out.println("\nCollected " + results.getRecords().orElse(0) + " total records");
              results.getAllRecords().ifPresent(records ->
                  records.forEach(record -> System.out.println("  " + record.getRecordTitle()))
              );

          } catch (NewscatcherApiApiException e) {
              System.err.println("Status: " + e.statusCode());
              System.err.println("Error: " + e.body());
          }
      }
  }
  ```
</Accordion>

## Company watchlist

Company watchlist lets you track specific companies across jobs. Create entities,
group them into a dataset, then connect the dataset to any job to get per-company
relevance scores in results.

<Tabs>
  <Tab title="Create entity">
    ```java theme={null}
    import com.newscatcher.catchall.CatchAllApi;
    import com.newscatcher.catchall.types.AdditionalAttributes;
    import com.newscatcher.catchall.types.CompanyAttributes;
    import com.newscatcher.catchall.types.CreateEntityRequest;
    import com.newscatcher.catchall.types.CreateEntityResponse;
    import com.newscatcher.catchall.types.EntityType;
    import java.util.List;

    CatchAllApi client = CatchAllApi.builder()
        .apiKey("YOUR_API_KEY")
        .build();

    CreateEntityResponse entity = client.entities().createEntity(
        CreateEntityRequest.builder()
            .name("NewsCatcher")
            .entityType(EntityType.COMPANY)
            .description("AI-powered news data provider")
            .additionalAttributes(AdditionalAttributes.builder()
                .companyAttributes(CompanyAttributes.builder()
                    .domain("newscatcherapi.com")
                    .alternativeNames(List.of("NC", "NewsCatcher API"))
                    .keyPersons(List.of("Artem Bugara", "Maksym Sugonyaka"))
                    .build())
                .build())
            .build()
    );
    String entityId = entity.getId();
    ```
  </Tab>

  <Tab title="Create dataset from CSV">
    Upload a CSV to create a dataset and entities in one step:

    ```java theme={null}
    import com.newscatcher.catchall.resources.datasets.requests.CreateDatasetFromCsvRequest;
    import com.newscatcher.catchall.types.CreateDatasetCsvResponse;
    import java.io.File;

    CreateDatasetCsvResponse dataset = client.datasets().createDatasetFromCsv(
        new File("companies.csv"),
        CreateDatasetFromCsvRequest.builder()
            .name("My Portfolio")
            .build()
    );
    String datasetId = dataset.getDatasetId();
    ```

    CSV format:

    ```csv theme={null}
    name,description,domain,alternative_names,key_persons
    NewsCatcher,"AI-powered news data provider",newscatcherapi.com,"NC;NewsCatcher API","Artem Bugara;Maksym Sugonyaka"
    OpenAI,"Artificial intelligence research company",openai.com,"Open AI","Sam Altman"
    ```
  </Tab>

  <Tab title="Submit connected job">
    Wait for the dataset to reach `ready` status, then submit a job:

    ```java theme={null}
    import com.newscatcher.catchall.types.DatasetResponse;
    import com.newscatcher.catchall.types.DatasetStatus;
    import com.newscatcher.catchall.resources.jobs.requests.SubmitRequestDto;
    import java.util.List;
    import java.util.concurrent.TimeUnit;

    // Poll until ready
    while (true) {
        DatasetResponse status = client.datasets().getDataset(datasetId);
        if (DatasetStatus.READY.equals(status.getLatestStatus().orElse(null))) break;
        TimeUnit.SECONDS.sleep(5);
    }

    // Submit connected job
    var job = client.jobs().createJob(
        SubmitRequestDto.builder()
            .query("AI chip partnerships")
            .connectedDatasetIds(List.of(datasetId))
            .build()
    );
    ```
  </Tab>

  <Tab title="Read results">
    Each record includes a `connected_entities` list with relevance scores:

    ```java theme={null}
    import com.newscatcher.catchall.types.PullJobResponseDto;

    PullJobResponseDto results = client.jobs().getJobResults(job.getJobId());

    results.getAllRecords().forEach(record -> {
        System.out.println(record.getRecordTitle());
        record.getConnectedEntities().ifPresent(entities ->
            entities.forEach(entity -> {
                System.out.printf("  %s: score %.0f/10%n",
                    entity.getName(), entity.getEdScore());
                System.out.println("  " + entity.getRelation());
            })
        );
    });
    ```
  </Tab>
</Tabs>

<Tip>
  For a full step-by-step walkthrough including batch entity creation and the
  JSON API path, see [Company Watchlist](/web-search-api/concepts/company-search).
</Tip>

## Error handling

Handle API errors with structured exception handling:

```java theme={null}
import com.newscatcher.catchall.core.NewscatcherApiApiException;

try {
    client.jobs().createJob(
        SubmitRequestDto.builder()
            .query("AI company acquisitions")
            .build()
    );
} catch (NewscatcherApiApiException e) {
    System.err.println("Status: " + e.statusCode());
    System.err.println("Error: " + e.body());
}
```

## Advanced usage

### Pagination

Retrieve large result sets page by page:

```java theme={null}
import com.newscatcher.catchall.resources.jobs.requests.GetJobResultsRequest;

int page = 1;
while (true) {
    var results = client.jobs().getJobResults(
        jobId,
        GetJobResultsRequest.builder()
            .page(page)
            .pageSize(100)
            .build()
    );

    System.out.println("Page " + page + "/" + results.getTotalPages().orElse(1));

    results.getAllRecords().ifPresent(records ->
        records.forEach(record ->
            System.out.println("  " + record.getRecordTitle())
        )
    );

    if (results.getPage().orElse(0) >= results.getTotalPages().orElse(1)) {
        break;
    }
    page++;
}
```

### Timeouts

Configure custom timeouts at client or request level:

<Tabs>
  <Tab title="Client-level">
    ```java theme={null}
    CatchAllApi client = CatchAllApi.builder()
        .apiKey("YOUR_API_KEY")
        .timeout(30)
        .build();
    ```
  </Tab>

  <Tab title="Request-level">
    ```java theme={null}
    import com.newscatcher.catchall.core.RequestOptions;

    client.jobs().createJob(
        SubmitRequestDto.builder()
            .query("AI company acquisitions")
            .build(),
        RequestOptions.builder()
            .timeout(10)
            .build()
    );
    ```
  </Tab>
</Tabs>

### Retries

Configure automatic retry behavior for failed requests:

<Tabs>
  <Tab title="Client-level">
    ```java theme={null}
    CatchAllApi client = CatchAllApi.builder()
        .apiKey("YOUR_API_KEY")
        .maxRetries(3)
        .build();
    ```
  </Tab>

  <Tab title="Request-level">
    ```java theme={null}
    client.jobs().createJob(
        SubmitRequestDto.builder()
            .query("AI company acquisitions")
            .build(),
        RequestOptions.builder()
            .maxRetries(3)
            .build()
    );
    ```
  </Tab>
</Tabs>

## Resources

* [GitHub Repository](https://github.com/Newscatcher/newscatcher-catchall-java)
* [Maven Central](https://central.sonatype.com/artifact/com.newscatcherapi/newscatcher-catchall-sdk)
* [API Reference](/web-search-api/api-reference/jobs/create-job)
* [Quickstart Guide](/web-search-api/get-started/quickstart)
