@@ -902,6 +902,116 @@ public CompletableFuture<BrowserListResponse> listBrowsersAsync(String status) {
902902 return CompletableFuture .supplyAsync (() -> listBrowsers (status ), asyncExecutor );
903903 }
904904
905+ /**
906+ * Asynchronously starts a crawl job and returns immediately with the job reference.
907+ *
908+ * @param url the URL to start crawling from
909+ * @param options crawl configuration options
910+ * @return a CompletableFuture that resolves to the CrawlResponse
911+ */
912+ public CompletableFuture <CrawlResponse > startCrawlAsync (String url , CrawlOptions options ) {
913+ return CompletableFuture .supplyAsync (() -> startCrawl (url , options ), asyncExecutor );
914+ }
915+
916+ /**
917+ * Asynchronously gets the status of a crawl job.
918+ *
919+ * @param jobId the crawl job ID
920+ * @return a CompletableFuture that resolves to the CrawlJob
921+ */
922+ public CompletableFuture <CrawlJob > getCrawlStatusAsync (String jobId ) {
923+ return CompletableFuture .supplyAsync (() -> getCrawlStatus (jobId ), asyncExecutor );
924+ }
925+
926+ /**
927+ * Asynchronously cancels a crawl job.
928+ *
929+ * @param jobId the crawl job ID
930+ * @return a CompletableFuture that resolves to the cancellation response
931+ */
932+ public CompletableFuture <Map <String , Object >> cancelCrawlAsync (String jobId ) {
933+ return CompletableFuture .supplyAsync (() -> cancelCrawl (jobId ), asyncExecutor );
934+ }
935+
936+ /**
937+ * Asynchronously starts a batch scrape job and returns immediately with the job reference.
938+ *
939+ * @param urls the URLs to scrape
940+ * @param options batch scrape configuration options
941+ * @return a CompletableFuture that resolves to the BatchScrapeResponse
942+ */
943+ public CompletableFuture <BatchScrapeResponse > startBatchScrapeAsync (List <String > urls , BatchScrapeOptions options ) {
944+ return CompletableFuture .supplyAsync (() -> startBatchScrape (urls , options ), asyncExecutor );
945+ }
946+
947+ /**
948+ * Asynchronously gets the status of a batch scrape job.
949+ *
950+ * @param jobId the batch scrape job ID
951+ * @return a CompletableFuture that resolves to the BatchScrapeJob
952+ */
953+ public CompletableFuture <BatchScrapeJob > getBatchScrapeStatusAsync (String jobId ) {
954+ return CompletableFuture .supplyAsync (() -> getBatchScrapeStatus (jobId ), asyncExecutor );
955+ }
956+
957+ /**
958+ * Asynchronously cancels a batch scrape job.
959+ *
960+ * @param jobId the batch scrape job ID
961+ * @return a CompletableFuture that resolves to the cancellation response
962+ */
963+ public CompletableFuture <Map <String , Object >> cancelBatchScrapeAsync (String jobId ) {
964+ return CompletableFuture .supplyAsync (() -> cancelBatchScrape (jobId ), asyncExecutor );
965+ }
966+
967+ /**
968+ * Asynchronously starts an agent task and returns immediately with the job reference.
969+ *
970+ * @param options agent configuration options
971+ * @return a CompletableFuture that resolves to the AgentResponse
972+ */
973+ public CompletableFuture <AgentResponse > startAgentAsync (AgentOptions options ) {
974+ return CompletableFuture .supplyAsync (() -> startAgent (options ), asyncExecutor );
975+ }
976+
977+ /**
978+ * Asynchronously gets the status of an agent task.
979+ *
980+ * @param jobId the agent job ID
981+ * @return a CompletableFuture that resolves to the AgentStatusResponse
982+ */
983+ public CompletableFuture <AgentStatusResponse > getAgentStatusAsync (String jobId ) {
984+ return CompletableFuture .supplyAsync (() -> getAgentStatus (jobId ), asyncExecutor );
985+ }
986+
987+ /**
988+ * Asynchronously cancels an agent task.
989+ *
990+ * @param jobId the agent job ID
991+ * @return a CompletableFuture that resolves to the cancellation response
992+ */
993+ public CompletableFuture <Map <String , Object >> cancelAgentAsync (String jobId ) {
994+ return CompletableFuture .supplyAsync (() -> cancelAgent (jobId ), asyncExecutor );
995+ }
996+
997+ /**
998+ * Asynchronously gets concurrency info.
999+ *
1000+ * @return a CompletableFuture that resolves to the ConcurrencyCheck
1001+ */
1002+ public CompletableFuture <ConcurrencyCheck > getConcurrencyAsync () {
1003+ return CompletableFuture .supplyAsync (this ::getConcurrency , asyncExecutor );
1004+ }
1005+
1006+ /**
1007+ * Asynchronously gets credit usage.
1008+ *
1009+ * @return a CompletableFuture that resolves to the CreditUsage
1010+ */
1011+ public CompletableFuture <CreditUsage > getCreditUsageAsync () {
1012+ return CompletableFuture .supplyAsync (this ::getCreditUsage , asyncExecutor );
1013+ }
1014+
9051015 // ================================================================
9061016 // INTERNAL POLLING HELPERS
9071017 // ================================================================
@@ -1008,6 +1118,7 @@ private void sleep(int seconds) {
10081118 public static final class Builder {
10091119
10101120 private String apiKey ;
1121+ private boolean apiKeyExplicitlySet ;
10111122 private String apiUrl = DEFAULT_API_URL ;
10121123 private long timeoutMs = DEFAULT_TIMEOUT_MS ;
10131124 private int maxRetries = DEFAULT_MAX_RETRIES ;
@@ -1023,6 +1134,7 @@ private Builder() {}
10231134 */
10241135 public Builder apiKey (String apiKey ) {
10251136 this .apiKey = apiKey ;
1137+ this .apiKeyExplicitlySet = true ;
10261138 return this ;
10271139 }
10281140
@@ -1098,6 +1210,10 @@ public Builder httpClient(OkHttpClient httpClient) {
10981210
10991211 public FirecrawlClient build () {
11001212 String resolvedKey = apiKey ;
1213+ if (apiKeyExplicitlySet && (resolvedKey == null || resolvedKey .isBlank ())) {
1214+ throw new FirecrawlException (
1215+ "API key cannot be null or empty when explicitly provided." );
1216+ }
11011217 if (resolvedKey == null || resolvedKey .isBlank ()) {
11021218 resolvedKey = System .getenv ("FIRECRAWL_API_KEY" );
11031219 }
0 commit comments