higherdata 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -20878,7 +20878,7 @@ var init_generated = __esm({
20878
20878
  },
20879
20879
  {
20880
20880
  "name": "account.limits",
20881
- "description": "Returns the current plan rate limits, concurrency and monthly email verification allowance.",
20881
+ "description": "Returns the current plan rate limits, concurrency and available purchased verify credit balance.",
20882
20882
  "risk": "read",
20883
20883
  "scopes": [],
20884
20884
  "input": {
@@ -21063,7 +21063,7 @@ var init_generated = __esm({
21063
21063
  },
21064
21064
  {
21065
21065
  "name": "account.usage",
21066
- "description": "Returns monthly activity, the email verification allowance and its reset time.",
21066
+ "description": "Returns monthly activity, the available purchased verify credit balance.",
21067
21067
  "risk": "read",
21068
21068
  "scopes": [],
21069
21069
  "input": {
@@ -22526,7 +22526,7 @@ var init_generated = __esm({
22526
22526
  },
22527
22527
  {
22528
22528
  "name": "email.find",
22529
- "description": "Find a person's work email from company pattern evidence. Tries at most three candidates in order and stops at the first deliverable. Dry run only plans candidates. Use the returned requestId to read or continue pending work.",
22529
+ "description": "Find a person's work email from company pattern evidence. Tries at most three candidates in order and stops at the first deliverable. At zero verify credits, returns the best available pattern guess marked unverified. Dry run only plans candidates. Use the returned requestId to read or continue pending work.",
22530
22530
  "risk": "read",
22531
22531
  "scopes": [
22532
22532
  "data:read"
@@ -22537,6 +22537,11 @@ var init_generated = __esm({
22537
22537
  {
22538
22538
  "type": "object",
22539
22539
  "properties": {
22540
+ "personId": {
22541
+ "description": "Stable person id to attach the verified email to.",
22542
+ "type": "string",
22543
+ "pattern": "^hdp_[0-9a-f]{32}$"
22544
+ },
22540
22545
  "firstName": {
22541
22546
  "type": "string",
22542
22547
  "minLength": 1,
@@ -22575,6 +22580,11 @@ var init_generated = __esm({
22575
22580
  "minLength": 1,
22576
22581
  "maxLength": 240
22577
22582
  },
22583
+ "linkedinUrl": {
22584
+ "description": "LinkedIn company page URL.",
22585
+ "type": "string",
22586
+ "maxLength": 2048
22587
+ },
22578
22588
  "city": {
22579
22589
  "description": "City of the headquarters.",
22580
22590
  "type": "string",
@@ -23124,7 +23134,7 @@ var init_generated = __esm({
23124
23134
  }
23125
23135
  },
23126
23136
  "additionalProperties": false,
23127
- "description": "Company to match. Supply an id, a domain, or a name."
23137
+ "description": "Company to match. Supply an id, a domain, a LinkedIn URL, or a name."
23128
23138
  }
23129
23139
  },
23130
23140
  "required": [
@@ -23154,7 +23164,7 @@ var init_generated = __esm({
23154
23164
  },
23155
23165
  {
23156
23166
  "name": "email.verify",
23157
- "description": "Verify one email address. Dry run spends nothing. Returns a requestId for pending work; call this tool with requestId to read or continue it. Verification results use deliverable, undeliverable, risky or unknown.",
23167
+ "description": "Verify one email address not already held for a contact. API callers may explicitly pass force to verify an existing address. Provider answers use purchased verify credits; cached answers are free. Dry run spends nothing. Returns a requestId for pending work; call this tool with requestId to read or continue it. Verification results use deliverable, undeliverable, risky or unknown.",
23158
23168
  "risk": "read",
23159
23169
  "scopes": [
23160
23170
  "data:read"
@@ -23165,12 +23175,21 @@ var init_generated = __esm({
23165
23175
  {
23166
23176
  "type": "object",
23167
23177
  "properties": {
23178
+ "personId": {
23179
+ "description": "Stable person id to attach the verified email to.",
23180
+ "type": "string",
23181
+ "pattern": "^hdp_[0-9a-f]{32}$"
23182
+ },
23168
23183
  "email": {
23169
23184
  "type": "string",
23170
23185
  "maxLength": 320,
23171
23186
  "format": "email",
23172
23187
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
23173
23188
  "description": "Work email address to verify."
23189
+ },
23190
+ "force": {
23191
+ "description": "API override to verify an address already held for a contact. Not offered in the UI.",
23192
+ "type": "boolean"
23174
23193
  }
23175
23194
  },
23176
23195
  "required": [
@@ -23379,7 +23398,7 @@ var init_generated = __esm({
23379
23398
  },
23380
23399
  {
23381
23400
  "name": "enrich.company",
23382
- "description": "Compare a company record with HigherData Core, using stable id, exact domain, then exact name and location. Name matches always require review. Dry run returns comparisons without recording them. Insights fields and filters require the Insights or Unlimited plan.",
23401
+ "description": "Compare a company record with HigherData Core, using stable id, LinkedIn URL, exact domain, then exact name and location. Name matches always require review. Dry run returns comparisons without recording them. Insights fields and filters require the Insights or Complete plan.",
23383
23402
  "risk": "write_local",
23384
23403
  "scopes": [
23385
23404
  "data:read",
@@ -23406,6 +23425,11 @@ var init_generated = __esm({
23406
23425
  "minLength": 1,
23407
23426
  "maxLength": 240
23408
23427
  },
23428
+ "linkedinUrl": {
23429
+ "description": "LinkedIn company page URL.",
23430
+ "type": "string",
23431
+ "maxLength": 2048
23432
+ },
23409
23433
  "city": {
23410
23434
  "description": "City of the headquarters.",
23411
23435
  "type": "string",
@@ -23955,7 +23979,7 @@ var init_generated = __esm({
23955
23979
  }
23956
23980
  },
23957
23981
  "additionalProperties": false,
23958
- "description": "Company to match. Supply an id, a domain, or a name."
23982
+ "description": "Company to match. Supply an id, a domain, a LinkedIn URL, or a name."
23959
23983
  }
23960
23984
  },
23961
23985
  {
@@ -24029,8 +24053,7 @@ var init_generated = __esm({
24029
24053
  "linkedinUrl": {
24030
24054
  "description": "LinkedIn person profile URL.",
24031
24055
  "type": "string",
24032
- "maxLength": 2048,
24033
- "format": "uri"
24056
+ "maxLength": 2048
24034
24057
  },
24035
24058
  "name": {
24036
24059
  "description": "Person's full name.",
@@ -24039,7 +24062,7 @@ var init_generated = __esm({
24039
24062
  "maxLength": 240
24040
24063
  },
24041
24064
  "company": {
24042
- "description": "Company this person works at. Supply an id, a domain, or a name.",
24065
+ "description": "Company this person works at. Supply an id, a domain, a LinkedIn URL, or a name.",
24043
24066
  "type": "object",
24044
24067
  "properties": {
24045
24068
  "id": {
@@ -24059,6 +24082,11 @@ var init_generated = __esm({
24059
24082
  "minLength": 1,
24060
24083
  "maxLength": 240
24061
24084
  },
24085
+ "linkedinUrl": {
24086
+ "description": "LinkedIn company page URL.",
24087
+ "type": "string",
24088
+ "maxLength": 2048
24089
+ },
24062
24090
  "city": {
24063
24091
  "description": "City of the headquarters.",
24064
24092
  "type": "string",
@@ -26713,6 +26741,14 @@ var init_generated = __esm({
26713
26741
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
26714
26742
  }
26715
26743
  },
26744
+ "sort": {
26745
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
26746
+ "type": "string",
26747
+ "enum": [
26748
+ "company_size_desc",
26749
+ "company_size_asc"
26750
+ ]
26751
+ },
26716
26752
  "firstName": {
26717
26753
  "description": "First name tokens, using accent-folded fuzzy matching. Requires companyId.",
26718
26754
  "type": "string",
@@ -26841,7 +26877,7 @@ var init_generated = __esm({
26841
26877
  },
26842
26878
  {
26843
26879
  "name": "lists.export",
26844
- "description": "Export a saved list as paged CSV or JSONL with opaque ids. CSV escapes spreadsheet formulas. Pages contain at most 50 records.Insights fields and filters require the Insights or Unlimited plan.",
26880
+ "description": "Export a saved list as paged CSV or JSONL with opaque ids. CSV escapes spreadsheet formulas. Pages contain at most 50 records.Insights fields and filters require the Insights or Complete plan.",
26845
26881
  "risk": "read",
26846
26882
  "scopes": [
26847
26883
  "data:read"
@@ -27030,7 +27066,7 @@ var init_generated = __esm({
27030
27066
  },
27031
27067
  {
27032
27068
  "name": "lookup.company",
27033
- "description": "Look up a company by exactly one domain, exact or near name (optionally with location), or opaque hdo_ id. Returns bounded candidates and ambiguity metadata. Insights fields and filters require the Insights or Unlimited plan.",
27069
+ "description": "Look up a company by exactly one domain, exact or near name (optionally with location), LinkedIn company page URL, or opaque hdo_ id, or up to 100 ids. Returns bounded candidates and ambiguity metadata. Insights fields and filters require the Insights or Complete plan.",
27034
27070
  "risk": "read",
27035
27071
  "scopes": [
27036
27072
  "data:read"
@@ -27055,7 +27091,7 @@ var init_generated = __esm({
27055
27091
  "maximum": 5
27056
27092
  },
27057
27093
  "excludeListId": {
27058
- "description": "Saved list whose members are left out of the result.",
27094
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27059
27095
  "type": "string",
27060
27096
  "format": "uuid",
27061
27097
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27098,7 +27134,7 @@ var init_generated = __esm({
27098
27134
  "maximum": 5
27099
27135
  },
27100
27136
  "excludeListId": {
27101
- "description": "Saved list whose members are left out of the result.",
27137
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27102
27138
  "type": "string",
27103
27139
  "format": "uuid",
27104
27140
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27118,7 +27154,7 @@ var init_generated = __esm({
27118
27154
  "description": "Opaque company id (hdo_)."
27119
27155
  },
27120
27156
  "excludeListId": {
27121
- "description": "Saved list whose members are left out of the result.",
27157
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27122
27158
  "type": "string",
27123
27159
  "format": "uuid",
27124
27160
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27128,6 +27164,59 @@ var init_generated = __esm({
27128
27164
  "id"
27129
27165
  ],
27130
27166
  "additionalProperties": false
27167
+ },
27168
+ {
27169
+ "type": "object",
27170
+ "properties": {
27171
+ "ids": {
27172
+ "minItems": 1,
27173
+ "maxItems": 100,
27174
+ "type": "array",
27175
+ "items": {
27176
+ "type": "string",
27177
+ "pattern": "^hdo_[0-9a-f]{32}$",
27178
+ "description": "Opaque company id (hdo_)."
27179
+ },
27180
+ "description": "Look up up to 100 opaque company ids in one call."
27181
+ },
27182
+ "excludeListId": {
27183
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27184
+ "type": "string",
27185
+ "format": "uuid",
27186
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27187
+ }
27188
+ },
27189
+ "required": [
27190
+ "ids"
27191
+ ],
27192
+ "additionalProperties": false
27193
+ },
27194
+ {
27195
+ "type": "object",
27196
+ "properties": {
27197
+ "linkedinUrl": {
27198
+ "type": "string",
27199
+ "maxLength": 2048,
27200
+ "description": "LinkedIn company page URL."
27201
+ },
27202
+ "candidateLimit": {
27203
+ "default": 5,
27204
+ "description": "Maximum candidates to return.",
27205
+ "type": "integer",
27206
+ "minimum": 1,
27207
+ "maximum": 5
27208
+ },
27209
+ "excludeListId": {
27210
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27211
+ "type": "string",
27212
+ "format": "uuid",
27213
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27214
+ }
27215
+ },
27216
+ "required": [
27217
+ "linkedinUrl"
27218
+ ],
27219
+ "additionalProperties": false
27131
27220
  }
27132
27221
  ]
27133
27222
  }
@@ -27143,6 +27232,12 @@ var init_generated = __esm({
27143
27232
  "$schema": "https://json-schema.org/draft/2020-12/schema",
27144
27233
  "type": "object",
27145
27234
  "properties": {
27235
+ "excludeListId": {
27236
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27237
+ "type": "string",
27238
+ "format": "uuid",
27239
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27240
+ },
27146
27241
  "companyId": {
27147
27242
  "description": "Select a company from a previous ambiguous company-scoped lookup.",
27148
27243
  "type": "string",
@@ -27189,7 +27284,7 @@ var init_generated = __esm({
27189
27284
  "maximum": 5
27190
27285
  },
27191
27286
  "excludeListId": {
27192
- "description": "Saved list whose members are left out of the result.",
27287
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27193
27288
  "type": "string",
27194
27289
  "format": "uuid",
27195
27290
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27216,7 +27311,7 @@ var init_generated = __esm({
27216
27311
  "maximum": 5
27217
27312
  },
27218
27313
  "excludeListId": {
27219
- "description": "Saved list whose members are left out of the result.",
27314
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27220
27315
  "type": "string",
27221
27316
  "format": "uuid",
27222
27317
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27256,7 +27351,7 @@ var init_generated = __esm({
27256
27351
  "maximum": 20
27257
27352
  },
27258
27353
  "excludeListId": {
27259
- "description": "Saved list whose members are left out of the result.",
27354
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27260
27355
  "type": "string",
27261
27356
  "format": "uuid",
27262
27357
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27273,7 +27368,6 @@ var init_generated = __esm({
27273
27368
  "linkedinUrl": {
27274
27369
  "type": "string",
27275
27370
  "maxLength": 2048,
27276
- "format": "uri",
27277
27371
  "description": "LinkedIn person profile URL."
27278
27372
  },
27279
27373
  "candidateLimit": {
@@ -27284,7 +27378,7 @@ var init_generated = __esm({
27284
27378
  "maximum": 5
27285
27379
  },
27286
27380
  "excludeListId": {
27287
- "description": "Saved list whose members are left out of the result.",
27381
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27288
27382
  "type": "string",
27289
27383
  "format": "uuid",
27290
27384
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
@@ -27311,10 +27405,14 @@ var init_generated = __esm({
27311
27405
  "maximum": 5
27312
27406
  },
27313
27407
  "excludeListId": {
27314
- "description": "Saved list whose members are left out of the result.",
27408
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
27315
27409
  "type": "string",
27316
27410
  "format": "uuid",
27317
27411
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27412
+ },
27413
+ "includeEmployment": {
27414
+ "description": "Include current and past jobs for this person, with suppression applied to each job.",
27415
+ "type": "boolean"
27318
27416
  }
27319
27417
  },
27320
27418
  "required": [
@@ -27327,7 +27425,7 @@ var init_generated = __esm({
27327
27425
  },
27328
27426
  {
27329
27427
  "name": "search.companies",
27330
- "description": "Find companies in HigherData Core using explicit filters. Optional domain uses exact website-host matching and ranks root sites first. Pages contain at most 50 records and opaque ids. Insights fields and filters require the Insights or Unlimited plan.",
27428
+ "description": "Find companies in HigherData Core using explicit filters. Optional domain uses exact website-host matching and ranks root sites first. Interactive pages contain at most 50 records. A download (download: true) contains at most 500 records and opaque ids. Insights fields and filters require the Insights or Complete plan.",
27331
27429
  "risk": "read",
27332
27430
  "scopes": [
27333
27431
  "data:read"
@@ -27678,10 +27776,10 @@ var init_generated = __esm({
27678
27776
  },
27679
27777
  "limit": {
27680
27778
  "default": 50,
27681
- "description": "Maximum records in this page.",
27779
+ "description": "Maximum records in this page. Interactive search accepts at most 50. A download (download true) accepts at most 500.",
27682
27780
  "type": "integer",
27683
27781
  "minimum": 1,
27684
- "maximum": 50
27782
+ "maximum": 500
27685
27783
  },
27686
27784
  "cursor": {
27687
27785
  "description": "Cursor from the previous page. Omit it for the first page.",
@@ -28371,10 +28469,14 @@ var init_generated = __esm({
28371
28469
  "additionalProperties": false
28372
28470
  },
28373
28471
  "excludeListId": {
28374
- "description": "Saved list whose members are left out of the result.",
28472
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
28375
28473
  "type": "string",
28376
28474
  "format": "uuid",
28377
28475
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
28476
+ },
28477
+ "download": {
28478
+ "description": "Set true to download a page. Interactive search stays at 50 records. The server reads this flag from the request, not from a plan or role.",
28479
+ "type": "boolean"
28378
28480
  }
28379
28481
  },
28380
28482
  "additionalProperties": false
@@ -29418,7 +29520,7 @@ var init_generated = __esm({
29418
29520
  },
29419
29521
  {
29420
29522
  "name": "search.count_people",
29421
- "description": "Count people once by primary current job before suppression using release-built aggregates; search results may match other current jobs. Unsupported refinements return a qualified baseline. exact: true explicitly requests one capped detailed count of matching people; never use it for live filtering.",
29523
+ "description": "Count people once by primary current job before suppression using release-built aggregates; search results may match other current jobs. Names and email use exact indexed counts. Unsupported refinements return an unavailable count, never a total that ignores them. exact: true explicitly requests one capped detailed count of matching people; never use it for live filtering.",
29422
29524
  "risk": "read",
29423
29525
  "scopes": [
29424
29526
  "data:read"
@@ -30133,7 +30235,7 @@ var init_generated = __esm({
30133
30235
  },
30134
30236
  "exact": {
30135
30237
  "default": false,
30136
- "description": "Explicitly run a detailed capped count instead of a rollup estimate. Never enable for live filtering.",
30238
+ "description": "Explicitly request a detailed count when an indexed count is unavailable. Never enable for live filtering.",
30137
30239
  "type": "boolean"
30138
30240
  }
30139
30241
  },
@@ -30244,7 +30346,7 @@ var init_generated = __esm({
30244
30346
  },
30245
30347
  {
30246
30348
  "name": "search.people",
30247
- "description": "Find people in HigherData Core, with 1 to 5 positive company HQ country codes or industries required unless a company id, company name, domain or indexed email scopes the search. Unscoped person names use indexed tokens and bounded, ranked pages. Person, job and company location use distinct filters; each city filter requires its corresponding country. Company-scoped contacts also support legacy contactLocation filters. Pages contain at most 50 people and opaque ids. ",
30349
+ "description": "Find people in HigherData Core, with 1 to 5 positive company HQ country codes or industries required unless a company id, company name, domain or indexed email scopes the search. Unscoped person names use indexed tokens and bounded, ranked pages. Person, job and company location use distinct filters; each city filter requires its corresponding country. Company-scoped contacts also support legacy contactLocation filters. Interactive pages contain at most 50 people. A download (download: true) contains at most 500 people and opaque ids. ",
30248
30350
  "risk": "read",
30249
30351
  "scopes": [
30250
30352
  "data:read"
@@ -30890,12 +30992,20 @@ var init_generated = __esm({
30890
30992
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
30891
30993
  }
30892
30994
  },
30995
+ "sort": {
30996
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
30997
+ "type": "string",
30998
+ "enum": [
30999
+ "company_size_desc",
31000
+ "company_size_asc"
31001
+ ]
31002
+ },
30893
31003
  "limit": {
30894
31004
  "default": 50,
30895
- "description": "Maximum records in this page.",
31005
+ "description": "Maximum records in this page. Interactive search accepts at most 50. A download (download true) accepts at most 500.",
30896
31006
  "type": "integer",
30897
31007
  "minimum": 1,
30898
- "maximum": 50
31008
+ "maximum": 500
30899
31009
  },
30900
31010
  "cursor": {
30901
31011
  "description": "Cursor from the previous page. Omit it for the first page.",
@@ -30964,10 +31074,14 @@ var init_generated = __esm({
30964
31074
  "additionalProperties": false
30965
31075
  },
30966
31076
  "excludeListId": {
30967
- "description": "Saved list whose members are left out of the result.",
31077
+ "description": "Ignore one suppression list for this call only, tenant owner only. Allowed kinds: customers, open_deals, competitors, partners, employees, custom. Do not contact, opt-out and global erasure rules cannot be lifted.",
30968
31078
  "type": "string",
30969
31079
  "format": "uuid",
30970
31080
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
31081
+ },
31082
+ "download": {
31083
+ "description": "Set true to download a page. Interactive search stays at 50 records. The server reads this flag from the request, not from a plan or role.",
31084
+ "type": "boolean"
30971
31085
  }
30972
31086
  },
30973
31087
  "additionalProperties": false
@@ -33754,6 +33868,14 @@ var init_generated = __esm({
33754
33868
  "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
33755
33869
  }
33756
33870
  },
33871
+ "sort": {
33872
+ "description": "Sort by primary company employee estimate, largest or smallest first. Unknown sizes come last. Available from the November 2026 release.",
33873
+ "type": "string",
33874
+ "enum": [
33875
+ "company_size_desc",
33876
+ "company_size_asc"
33877
+ ]
33878
+ },
33757
33879
  "firstName": {
33758
33880
  "description": "First name tokens, using accent-folded fuzzy matching. Requires companyId.",
33759
33881
  "type": "string",
@@ -34194,6 +34316,14 @@ var init_generated = __esm({
34194
34316
  });
34195
34317
 
34196
34318
  // ../sdk/src/transport.ts
34319
+ function responseByteLimit(path2, input2) {
34320
+ const name2 = path2.split("?")[0]?.split("/").at(-1);
34321
+ if (name2 !== "search.companies" && name2 !== "search.people") return MAX_RESPONSE;
34322
+ if (!input2 || typeof input2 !== "object" || Array.isArray(input2) || input2.download !== true) return MAX_RESPONSE;
34323
+ const limit = input2.limit;
34324
+ const rows = typeof limit === "number" && Number.isInteger(limit) && limit >= 1 ? Math.min(limit, DOWNLOAD_PAGE_ROWS) : INTERACTIVE_PAGE_ROWS;
34325
+ return MAX_RESPONSE * Math.ceil(rows / INTERACTIVE_PAGE_ROWS);
34326
+ }
34197
34327
  function retryAfter(response, data, now) {
34198
34328
  const value = response.headers.get("retry-after");
34199
34329
  if (value !== null) {
@@ -34210,9 +34340,9 @@ function retryAfter(response, data, now) {
34210
34340
  }
34211
34341
  function apiError(response, data, now) {
34212
34342
  const parsed = failure2.safeParse(data), error63 = parsed.success ? parsed.data.error : null;
34213
- return new HigherDataError(typeof error63 === "object" && error63?.message ? error63.message : "The HigherData API request failed.", typeof error63 === "string" ? error63 : error63?.code ?? "http_error", response.status, retryAfter(response, data, now), response.headers.get("x-request-id") ?? void 0);
34343
+ return new HigherDataError(typeof error63 === "object" && error63?.message ? error63.message : "The HigherData API request failed.", typeof error63 === "string" ? error63 : error63?.code ?? "http_error", response.status, retryAfter(response, data, now), response.headers.get("x-request-id") ?? void 0, typeof error63 === "object" ? error63?.billing_href : void 0);
34214
34344
  }
34215
- async function responseJson(response) {
34345
+ async function responseJson(response, maxBytes) {
34216
34346
  if (!response.body) throw new HigherDataError("The API returned an invalid response.", "invalid_response");
34217
34347
  const reader = response.body.getReader(), chunks = [];
34218
34348
  let length = 0;
@@ -34221,7 +34351,7 @@ async function responseJson(response) {
34221
34351
  const { done, value } = await reader.read();
34222
34352
  if (done) break;
34223
34353
  length += value.byteLength;
34224
- if (length > MAX_RESPONSE) {
34354
+ if (length > maxBytes) {
34225
34355
  await reader.cancel();
34226
34356
  throw new HigherDataError("The API response exceeded the size limit.", "response_too_large");
34227
34357
  }
@@ -34258,28 +34388,32 @@ function delay(ms, signal) {
34258
34388
  signal?.addEventListener("abort", abort, { once: true });
34259
34389
  });
34260
34390
  }
34261
- var failure2, HigherDataError, limits, MAX_RESPONSE, JsonTransport;
34391
+ var failure2, HigherDataError, limits, MAX_RESPONSE, DOWNLOAD_PAGE_ROWS, INTERACTIVE_PAGE_ROWS, JsonTransport;
34262
34392
  var init_transport = __esm({
34263
34393
  "../sdk/src/transport.ts"() {
34264
34394
  "use strict";
34265
34395
  init_zod();
34266
- failure2 = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string().max(128), message: external_exports.string().max(2048).optional(), retry_after: external_exports.number().nonnegative().finite().optional() })]) });
34396
+ failure2 = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string().max(128), message: external_exports.string().max(2048).optional(), billing_href: external_exports.string().max(2048).optional(), retry_after: external_exports.number().nonnegative().finite().optional() })]) });
34267
34397
  HigherDataError = class extends Error {
34268
- constructor(message, code, status = 0, retryAfter2, requestId) {
34398
+ constructor(message, code, status = 0, retryAfter2, requestId, billingHref) {
34269
34399
  super(message);
34270
34400
  this.code = code;
34271
34401
  this.status = status;
34272
34402
  this.retryAfter = retryAfter2;
34273
34403
  this.requestId = requestId;
34404
+ this.billingHref = billingHref;
34274
34405
  this.name = "HigherDataError";
34275
34406
  }
34276
34407
  code;
34277
34408
  status;
34278
34409
  retryAfter;
34279
34410
  requestId;
34411
+ billingHref;
34280
34412
  };
34281
34413
  limits = external_exports.object({ maxRetries: external_exports.number().int().min(0).max(5), timeoutMs: external_exports.number().int().min(1).max(3e5), maxRetryDelayMs: external_exports.number().int().min(0).max(3e5) });
34282
34414
  MAX_RESPONSE = 8 * 1024 * 1024;
34415
+ DOWNLOAD_PAGE_ROWS = 500;
34416
+ INTERACTIVE_PAGE_ROWS = 50;
34283
34417
  JsonTransport = class {
34284
34418
  constructor(options) {
34285
34419
  this.options = options;
@@ -34317,7 +34451,7 @@ var init_transport = __esm({
34317
34451
  const timeout = AbortSignal.timeout(options.timeoutMs ?? this.settings.timeoutMs);
34318
34452
  response = await (this.options.fetch ?? fetch)(url2.toString(), { method: "POST", headers, body, redirect: "manual", signal: options.signal ? AbortSignal.any([timeout, options.signal]) : timeout });
34319
34453
  try {
34320
- data = await responseJson(response);
34454
+ data = await responseJson(response, responseByteLimit(path2, input2));
34321
34455
  } catch (error64) {
34322
34456
  if (response.ok) throw error64;
34323
34457
  data = void 0;
@@ -34377,6 +34511,8 @@ var init_exit_codes = __esm({
34377
34511
  function apiFailure(status, data) {
34378
34512
  const parsed = external_exports.object({ error: external_exports.union([external_exports.string(), external_exports.object({ code: external_exports.string(), message: external_exports.string().optional() })]) }).safeParse(data);
34379
34513
  const code = parsed.success ? (typeof parsed.data.error === "string" ? parsed.data.error : parsed.data.error.code).toLowerCase() : "";
34514
+ if (code === "credits_required") return new CliError("Email verification is paused for credit.", EXIT_CODES.LIMIT);
34515
+ if (code === "plan_required") return new CliError("Your plan does not include this feature.");
34380
34516
  if (status === 401 || status === 403 || ["unauthorized", "forbidden"].includes(code)) return new CliError("Authentication failed. Run higherdata login or check HIGHERDATA_API_KEY and the selected space.", EXIT_CODES.AUTH);
34381
34517
  const message = parsed.success && typeof parsed.data.error !== "string" ? parsed.data.error.message : void 0;
34382
34518
  if (code === "limit_exceeded" && message && /^This month's email verification allowance is reached\. Resets \d{4}-\d{2}-\d{2}\. Upgrade: (https:\/\/[^\s]+|\/docs\/plans-and-limits)$/.test(message)) return new CliError(message, EXIT_CODES.LIMIT);
@@ -34409,7 +34545,7 @@ var init_api2 = __esm({
34409
34545
  ApiClient = class {
34410
34546
  constructor(settings, fetchImpl = fetch) {
34411
34547
  this.settings = settings;
34412
- this.transport = new JsonTransport({ ...settings, fetch: fetchImpl, maxRetries: 0 });
34548
+ this.transport = new JsonTransport({ ...settings, fetch: (url2, init) => fetchImpl(url2, { ...init, headers: { ...init?.headers, "x-higherdata-client": "cli" } }), maxRetries: 0 });
34413
34549
  }
34414
34550
  settings;
34415
34551
  transport;
@@ -34431,7 +34567,7 @@ var init_api2 = __esm({
34431
34567
  if (error63.status) {
34432
34568
  const mapped = apiFailure(error63.status, { error: { code: error63.code, message: error63.message } });
34433
34569
  const plain = (value) => (this.settings.apiKey ? value.replaceAll(this.settings.apiKey, "[redacted]") : value).replace(/[\u0000-\u001f\u007f-\u009f]/gu, " ");
34434
- throw new CliError(`${plain(error63.code)}: ${plain(error63.message === "The HigherData API request failed." ? mapped.message : error63.message)}${error63.requestId ? ` (request: ${plain(error63.requestId)})` : ""}`, mapped.exitCode);
34570
+ throw new CliError(`${plain(error63.code)}: ${plain(error63.message === "The HigherData API request failed." ? mapped.message : error63.message)}${error63.billingHref ? ` Buy verify credits: ${plain(error63.billingHref)}` : ""}${error63.requestId ? ` (request: ${plain(error63.requestId)})` : ""}`, mapped.exitCode);
34435
34571
  }
34436
34572
  if (error63.code === "response_too_large") throw new CliError("The API response exceeded the CLI size limit.");
34437
34573
  throw new CliError(error63.message);
@@ -38002,7 +38138,7 @@ var init_package = __esm({
38002
38138
  "package.json"() {
38003
38139
  package_default = {
38004
38140
  name: "higherdata",
38005
- version: "0.6.0",
38141
+ version: "0.7.0",
38006
38142
  type: "module",
38007
38143
  bin: {
38008
38144
  higherdata: "dist/index.js"
@@ -38052,7 +38188,7 @@ var init_package = __esm({
38052
38188
  directory: "packages/cli"
38053
38189
  },
38054
38190
  higherdata: {
38055
- minimumServerVersion: "0.2.0"
38191
+ minimumServerVersion: "0.7.0"
38056
38192
  },
38057
38193
  optionalDependencies: {
38058
38194
  "@napi-rs/keyring": "2.1.0"
@@ -39875,6 +40011,13 @@ var init_records = __esm({
39875
40011
  }
39876
40012
  });
39877
40013
 
40014
+ // ../../src/lib/lookup/linkedin.ts
40015
+ var init_linkedin = __esm({
40016
+ "../../src/lib/lookup/linkedin.ts"() {
40017
+ "use strict";
40018
+ }
40019
+ });
40020
+
39878
40021
  // ../../src/lib/lookup/phone.ts
39879
40022
  var init_phone = __esm({
39880
40023
  "../../src/lib/lookup/phone.ts"() {
@@ -40649,6 +40792,7 @@ var suppressionKeyTypeSchema;
40649
40792
  var init_normalize = __esm({
40650
40793
  "../../src/lib/suppression/normalize.ts"() {
40651
40794
  "use strict";
40795
+ init_linkedin();
40652
40796
  init_phone();
40653
40797
  init_es62();
40654
40798
  init_zod();
@@ -41051,28 +41195,9 @@ function pageCsv(headers, rows) {
41051
41195
  return `${lines.join("\r\n")}\r
41052
41196
  `;
41053
41197
  }
41054
- function companyPageCsv(items) {
41055
- return pageCsv(
41056
- [...COMPANY_DOWNLOAD_FIELDS],
41057
- items.map((item) => COMPANY_DOWNLOAD_FIELDS.map((field) => item[field]))
41058
- );
41059
- }
41060
- function peoplePageCsv(items) {
41061
- return pageCsv(
41062
- [...PEOPLE_DOWNLOAD_FIELDS],
41063
- items.map((item) => PEOPLE_DOWNLOAD_FIELDS.map((field) => item[field]))
41064
- );
41065
- }
41066
- function searchPageJsonl(kind, items) {
41067
- const fields = kind === "companies" ? COMPANY_DOWNLOAD_FIELDS : PEOPLE_DOWNLOAD_FIELDS;
41068
- return items.map((item) => JSON.stringify(Object.fromEntries(fields.map((field) => [field, item[field] ?? null]))) + "\n").join("");
41069
- }
41070
- var COMPANY_DOWNLOAD_FIELDS, PEOPLE_DOWNLOAD_FIELDS;
41071
41198
  var init_search_download = __esm({
41072
41199
  "../../src/lib/csv/search-download.ts"() {
41073
41200
  "use strict";
41074
- COMPANY_DOWNLOAD_FIELDS = ["id", "companyName", "normalizedDomain", "headquartersCountryCode", "industryLinkedin", "employeeCountEstimate"];
41075
- PEOPLE_DOWNLOAD_FIELDS = ["id", "fullName", "jobTitle", "companyName", "emailAddress", "countryCode"];
41076
41201
  }
41077
41202
  });
41078
41203
 
@@ -41082,60 +41207,168 @@ function searchDownloadFormat(path2) {
41082
41207
  if (!/\.(csv|jsonl)$/.test(path2)) throw new CliError("Choose a new .csv or .jsonl destination.");
41083
41208
  return path2.endsWith(".jsonl") ? "jsonl" : "csv";
41084
41209
  }
41210
+ function customerRecord(item) {
41211
+ const value = customerOutput(item);
41212
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new CliError("The API returned an invalid search page.");
41213
+ return value;
41214
+ }
41215
+ function csvCellValue(value) {
41216
+ return value !== null && typeof value === "object" ? JSON.stringify(value) : value;
41217
+ }
41218
+ function downloadRecordsJsonl(items) {
41219
+ return items.map((item) => `${JSON.stringify(customerRecord(item))}
41220
+ `).join("");
41221
+ }
41222
+ function downloadProgress(count, finishedAll) {
41223
+ const label = `${count.toLocaleString("en-US")} ${count === 1 ? "row" : "rows"}`;
41224
+ return finishedAll ? `${label} (all)` : label;
41225
+ }
41085
41226
  async function downloadSearch(api, name2, input2, path2, options) {
41086
41227
  const format2 = searchDownloadFormat(path2);
41087
41228
  if (options.all && api.settings.idempotencyKey) throw new CliError("An idempotency key applies to one page. Omit --all.");
41088
41229
  const file2 = await open5(path2, "wx", 384);
41089
- let count = 0, first = true, cursor = input2.cursor, loaded = options.loadedPage;
41090
- const seen = /* @__PURE__ */ new Set();
41091
- if (typeof cursor === "string") seen.add(cursor);
41092
- try {
41093
- while (true) {
41094
- let raw, headers;
41230
+ const seenCursors = /* @__PURE__ */ new Set();
41231
+ const seenIds = /* @__PURE__ */ new Set();
41232
+ const startCursor = typeof input2.cursor === "string" ? input2.cursor : void 0;
41233
+ if (startCursor) seenCursors.add(startCursor);
41234
+ const requestInput = { ...input2, ...options.all ? { limit: SEARCH_DOWNLOAD_PAGE_SIZE, download: true } : {} };
41235
+ delete requestInput.cursor;
41236
+ let loaded = options.loadedPage;
41237
+ let reserved = 0;
41238
+ let written = 0;
41239
+ let capped = false;
41240
+ let position = 0;
41241
+ const columns = [];
41242
+ const csvRows = [];
41243
+ const idPattern = name2 === "search.companies" ? /^hdo_[a-f0-9]{32}$/ : /^hdp_[a-f0-9]{32}$/;
41244
+ let ahead = { kind: "ready", cursor: startCursor };
41245
+ const queue = [];
41246
+ let failed = false;
41247
+ async function writeAt(text2, at) {
41248
+ const body = Buffer.from(text2);
41249
+ let offset = 0;
41250
+ while (offset < body.length) {
41251
+ const { bytesWritten } = await file2.write(body, offset, body.length - offset, at + offset);
41252
+ if (bytesWritten <= 0) throw new CliError("The download could not be written.");
41253
+ offset += bytesWritten;
41254
+ }
41255
+ return at + body.length;
41256
+ }
41257
+ function csvText(records) {
41258
+ return pageCsv(columns, records.map((record2) => columns.map((key) => csvCellValue(record2[key]))));
41259
+ }
41260
+ async function writeRecords(items) {
41261
+ if (!items.length) return;
41262
+ if (options.beforeWrite) await options.beforeWrite();
41263
+ if (format2 === "jsonl") {
41264
+ position = await writeAt(downloadRecordsJsonl(items), position);
41265
+ return;
41266
+ }
41267
+ const records = items.map(customerRecord);
41268
+ const seen = new Set(columns);
41269
+ const grew = columns.length > 0 && records.some((record2) => Object.keys(record2).some((key) => !seen.has(key)));
41270
+ for (const record2 of records) for (const key of Object.keys(record2)) if (!seen.has(key)) {
41271
+ seen.add(key);
41272
+ columns.push(key);
41273
+ }
41274
+ const first = csvRows.length === 0;
41275
+ csvRows.push(...records);
41276
+ if (first) position = await writeAt(csvText(records), position);
41277
+ else if (grew) {
41278
+ position = await writeAt(csvText(csvRows), 0);
41279
+ await file2.truncate(position);
41280
+ } else {
41281
+ const page = csvText(records);
41282
+ position = await writeAt(page.slice(page.indexOf("\r\n") + 2), position);
41283
+ }
41284
+ }
41285
+ async function pace(headers) {
41286
+ if (headers?.get("ratelimit-remaining") !== "0") return;
41287
+ const reset = headers.get("ratelimit-reset");
41288
+ if (reset !== null && reset !== void 0 && /^\d+(?:\.\d+)?$/.test(reset.trim())) await options.sleep(Number(reset) * 1e3);
41289
+ }
41290
+ function kick() {
41291
+ if (ahead.kind !== "ready" || queue.length >= SEARCH_DOWNLOAD_WINDOW || failed) return;
41292
+ const cursor = ahead.cursor;
41293
+ ahead = { kind: "wait" };
41294
+ const promise2 = fetchPage(cursor);
41295
+ void promise2.catch(() => void 0);
41296
+ queue.push(promise2);
41297
+ }
41298
+ async function fetchPage(cursor) {
41299
+ try {
41300
+ let raw;
41301
+ let headers;
41095
41302
  if (loaded) {
41096
41303
  raw = loaded;
41097
41304
  loaded = void 0;
41098
41305
  } else {
41099
- const response = await api.request(`/api/v1/tools/${name2}`, { ...input2, ...cursor === void 0 ? {} : { cursor } }, { authenticated: true, rateLimitRecovery: options });
41306
+ const response = await api.request(`/api/v1/tools/${name2}`, { ...requestInput, ...cursor === void 0 ? {} : { cursor } }, { authenticated: true, rateLimitRecovery: options });
41100
41307
  raw = response.data;
41101
41308
  headers = response.headers;
41102
41309
  }
41103
41310
  const page = pageSchema2.safeParse(raw);
41104
- if (!page.success || page.data.items.some((item) => typeof item.id !== "string" || !(name2 === "search.companies" ? /^hdo_[a-f0-9]{32}$/ : /^hdp_[a-f0-9]{32}$/).test(item.id))) throw new CliError("The API returned an invalid search page.");
41105
- const items = page.data.items.slice(0, SEARCH_DOWNLOAD_CAP - count);
41106
- if (format2 === "jsonl") await file2.writeFile(searchPageJsonl(name2 === "search.companies" ? "companies" : "people", items));
41107
- else {
41108
- const csv = (name2 === "search.companies" ? companyPageCsv : peoplePageCsv)(items);
41109
- await file2.writeFile(first ? csv : csv.slice(csv.indexOf("\r\n") + 2));
41110
- }
41111
- first = false;
41112
- count += items.length;
41113
- options.progress?.(`Downloaded ${count.toLocaleString("en-US")} of 5,000`);
41311
+ if (!page.success || page.data.items.some((item) => typeof item.id !== "string" || !idPattern.test(item.id))) throw new CliError("The API returned an invalid search page.");
41312
+ const fresh = [];
41313
+ for (const item of page.data.items) {
41314
+ const id = item.id;
41315
+ if (seenIds.has(id)) continue;
41316
+ seenIds.add(id);
41317
+ fresh.push(item);
41318
+ }
41319
+ const items = fresh.slice(0, Math.max(0, SEARCH_DOWNLOAD_CAP - reserved));
41320
+ reserved += items.length;
41114
41321
  const next = page.data.nextCursor;
41115
- if (!options.all || !next || count === SEARCH_DOWNLOAD_CAP) return { rows: count, capped: !!options.all && count === SEARCH_DOWNLOAD_CAP && (!!next || items.length < page.data.items.length) };
41116
- if (seen.has(next)) throw new CliError("The API repeated a cursor. Pagination stopped.");
41117
- seen.add(next);
41118
- cursor = next;
41119
- if (headers?.get("ratelimit-remaining") === "0") {
41120
- const reset = headers.get("ratelimit-reset");
41121
- if (reset !== null && /^\d+(?:\.\d+)?$/.test(reset.trim())) await options.sleep(Number(reset) * 1e3);
41122
- }
41322
+ const cappedPage = Boolean(options.all) && reserved === SEARCH_DOWNLOAD_CAP && (Boolean(next) || fresh.length > items.length);
41323
+ const stop = !options.all || !next || cappedPage;
41324
+ if (!stop && next) {
41325
+ if (seenCursors.has(next)) {
41326
+ ahead = { kind: "stop" };
41327
+ return { items, done: true, capped: cappedPage, error: new CliError("The API repeated a cursor. Pagination stopped.") };
41328
+ }
41329
+ seenCursors.add(next);
41330
+ await pace(headers);
41331
+ ahead = { kind: "ready", cursor: next };
41332
+ kick();
41333
+ } else ahead = { kind: "stop" };
41334
+ return { items, done: stop, capped: cappedPage };
41335
+ } catch (error63) {
41336
+ failed = true;
41337
+ ahead = { kind: "stop" };
41338
+ throw error63;
41123
41339
  }
41340
+ }
41341
+ try {
41342
+ kick();
41343
+ while (queue.length) {
41344
+ const planned = await queue[0];
41345
+ await writeRecords(planned.items);
41346
+ written += planned.items.length;
41347
+ if (planned.capped) capped = true;
41348
+ options.progress?.(downloadProgress(written, planned.done && Boolean(options.all) && !capped));
41349
+ queue.shift();
41350
+ kick();
41351
+ if (planned.error) throw planned.error;
41352
+ }
41353
+ return { rows: written, capped };
41124
41354
  } finally {
41125
41355
  await file2.close();
41126
41356
  }
41127
41357
  }
41128
- var SEARCH_DOWNLOAD_CAP, SEARCH_DOWNLOAD_CAP_NOTICE, isSearchResults, pageSchema2;
41358
+ var SEARCH_DOWNLOAD_CAP, SEARCH_DOWNLOAD_CAP_NOTICE, SEARCH_DOWNLOAD_PAGE_SIZE, SEARCH_DOWNLOAD_WINDOW, isSearchResults, pageSchema2;
41129
41359
  var init_search_download2 = __esm({
41130
41360
  "src/search-download.ts"() {
41131
41361
  "use strict";
41132
41362
  init_zod();
41133
41363
  init_search_download();
41134
41364
  init_api2();
41365
+ init_output();
41135
41366
  SEARCH_DOWNLOAD_CAP = 5e3;
41136
41367
  SEARCH_DOWNLOAD_CAP_NOTICE = "Download stopped at 5,000 rows. Save the search to a list and export the list for larger sets.";
41368
+ SEARCH_DOWNLOAD_PAGE_SIZE = 500;
41369
+ SEARCH_DOWNLOAD_WINDOW = 3;
41137
41370
  isSearchResults = (name2) => name2 === "search.companies" || name2 === "search.people";
41138
- pageSchema2 = external_exports.object({ items: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())).max(50), nextCursor: external_exports.string().min(1).max(2048).nullable() });
41371
+ pageSchema2 = external_exports.object({ items: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())).max(500), nextCursor: external_exports.string().min(1).max(2048).nullable() });
41139
41372
  }
41140
41373
  });
41141
41374
 
@@ -43893,7 +44126,7 @@ async function globalScript(runtime, env, script) {
43893
44126
  }
43894
44127
  }
43895
44128
  async function autoUpdate(input2) {
43896
- if (!interactiveUpdateAllowed(input2.args, input2.env, input2.runtime) || input2.env.HIGHERDATA_NO_AUTO_UPDATE === "1" || input2.env.HIGHERDATA_UPDATE_RERUN === "1" || ["update", "upgrade", "config"].includes(primaryCommand(input2.args) ?? "")) return null;
44129
+ if (!interactiveUpdateAllowed(input2.args, input2.env, input2.runtime) || input2.env.HIGHERDATA_NO_AUTO_UPDATE === "1" || input2.env.HIGHERDATA_UPDATE_RERUN === "1" || ["update", "upgrade", "config", "guide"].includes(primaryCommand(input2.args) ?? "")) return null;
43897
44130
  if ((await input2.readConfig()).autoUpdate === false) return null;
43898
44131
  const candidate = await possibleGlobalScript(input2.runtime, input2.env);
43899
44132
  if (!candidate) return null;
@@ -46497,13 +46730,14 @@ function fieldsFor(schema, input2 = {}, variant = 0, prefix = "") {
46497
46730
  const discriminant = Object.keys(branches[0].properties).find((key) => branches.every((s) => s.properties?.[key]?.const !== void 0));
46498
46731
  const matched = branches.findIndex((s) => (s.required ?? []).some((key) => Object.hasOwn(input2, key)));
46499
46732
  const index = discriminant ? Math.max(0, branches.findIndex((s) => s.properties[discriminant].const === input2[discriminant])) : matched >= 0 ? matched : Math.min(variant, branches.length - 1);
46500
- const choices = branches.map((s) => discriminant ? s.properties[discriminant].const : Object.keys(s.properties).map(title).join(" / "));
46733
+ const choices = branches.map((s) => discriminant ? s.properties[discriminant].const : Object.keys(s.properties).filter((key) => key !== "force").map(title).join(" / "));
46501
46734
  const selector = { path: prefix ? `${prefix}.$variant` : "$variant", label: "Input type", required: true, schema: { type: "string", enum: choices } };
46502
46735
  return [selector, ...fieldsFor(branches[index], input2, variant, prefix).filter((f) => !discriminant || f.path !== `${prefix ? `${prefix}.` : ""}${discriminant}`)];
46503
46736
  }
46504
46737
  return fieldsFor(branches[0] ?? {}, input2, variant, prefix);
46505
46738
  }
46506
46739
  return Object.entries(schema.properties ?? {}).flatMap(([key, raw]) => {
46740
+ if (key === "force") return [];
46507
46741
  const path2 = prefix ? `${prefix}.${key}` : key;
46508
46742
  const alternatives2 = raw.anyOf ?? raw.oneOf;
46509
46743
  const field = alternatives2?.length === 2 && alternatives2.some((s) => s.type === "null") ? alternatives2.find((s) => s.type !== "null") : raw;
@@ -46712,12 +46946,12 @@ async function finishEmailRequest(name2, result, poll, sleep) {
46712
46946
  }
46713
46947
  function registerToolCommands(program2, runtime) {
46714
46948
  const tools = program2.command("tools").description("Every HigherData tool, with schema-driven flags and JSON input");
46715
- for (const tool of TOOL_CATALOG) for (const parent of tool.name === "email.find" ? [tools, program2] : [tools]) {
46716
- const command = parent.command(parent === program2 ? "find-email" : tool.name).description(tool.description).option("--input <json>", "Tool input as a JSON object", inputObject, {}).option("--execute", "Execute an irreversible action or email request (default: preview)").option("--yes", "Confirm execution of this request");
46949
+ for (const tool of TOOL_CATALOG) for (const parent of tool.name.startsWith("email.") ? [tools, program2] : [tools]) {
46950
+ const command = parent.command(parent === program2 ? tool.name === "email.find" ? "find-email" : "verify-email" : tool.name).description(tool.description).option("--input <json>", "Tool input as a JSON object", inputObject, {}).option("--execute", "Execute an irreversible action or email request (default: preview)").option("--yes", "Confirm execution of this request");
46717
46951
  const flags = flagFields(tool.input).map((field, index) => {
46718
46952
  const flag2 = field.path.replace(/([a-z])([A-Z])/gu, "$1-$2").replaceAll(".", "-").replaceAll("_", "-").toLowerCase();
46719
46953
  const attribute = `field${index}`;
46720
- const option = new Option(`--${flag2} <value>`, `${field.schema.description ?? field.path}${field.schema.type === "array" ? " (comma separated strings or JSON array)" : ""}`);
46954
+ const option = new Option(field.path === "force" ? "--force" : `${field.path === "linkedinUrl" ? "--linkedin <value>, " : ""}--${flag2} <value>`, `${field.schema.description ?? field.path}${field.schema.type === "array" ? " (comma separated strings or JSON array)" : ""}`);
46721
46955
  option.attributeName = () => attribute;
46722
46956
  command.addOption(option);
46723
46957
  return { field, attribute };
@@ -46725,7 +46959,10 @@ function registerToolCommands(program2, runtime) {
46725
46959
  command.action(async (options, cmd) => {
46726
46960
  if (options.execute && needsConfirmation(tool) && !options.yes) throw new CliError("Review the dry run, then supply --execute --yes to confirm this request.");
46727
46961
  let input2 = options.input;
46728
- for (const { field, attribute } of flags) if (typeof options[attribute] === "string") input2 = putField(input2, field, options[attribute]);
46962
+ for (const { field, attribute } of flags) {
46963
+ if (typeof options[attribute] === "string") input2 = putField(input2, field, options[attribute]);
46964
+ else if (field.path === "force" && options[attribute] === true) input2 = { ...input2, force: true };
46965
+ }
46729
46966
  const globals = cmd.optsWithGlobals();
46730
46967
  const settings = resolveSettings(await runtime.readConfig(), globals, runtime.env);
46731
46968
  const api = new ApiClient({ ...settings, idempotencyKey: globals.idempotencyKey ?? randomUUID5() }, runtime.fetchImpl);
@@ -47413,7 +47650,7 @@ function TerminalScreen({ state, columns, rows = 28, colors = true, profile = "d
47413
47650
  isCore && (fullScreen ? /* @__PURE__ */ jsx5(Box4, { children: ["Overview", "Contacts", "Insights"].map((label, i) => /* @__PURE__ */ jsx5(Hit, { action: { key: `record-${i}` }, children: /* @__PURE__ */ jsx5(Text, { color, children: ` [${label}] ` }) }, label)) }) : /* @__PURE__ */ jsx5(Text, { color, children: "[1 Overview] [2 Contacts] [3 Insights]" })),
47414
47651
  state.tab === 2 && !row.insights ? /* @__PURE__ */ jsx5(Text, { children: "Insights requires an Insights or Unlimited plan, and a release with Insights fields. Check Account / Limits." }) : fieldLines(state.tool?.name.startsWith("changes.") ? reviewFields(row) : detail, width - 4).slice(state.selected, state.selected + limit - 2).map((line, i) => /* @__PURE__ */ jsx5(Text, { children: line }, i))
47415
47652
  ] });
47416
- help = isCore ? `Up/Down scroll. ${fullScreen ? "O/C/I record tabs." : "1/2/3 tabs."} A add to list. ${String(row.id).startsWith("hdp_") ? String(row.emailAddress ?? row.email ?? "").trim() ? "V verify email. " : "F find email. " : ""}Esc back.` : "Up/Down scroll. Esc back.";
47653
+ help = isCore ? `Up/Down scroll. ${fullScreen ? "O/C/I record tabs." : "1/2/3 tabs."} A add to list. ${String(row.id).startsWith("hdp_") && !String(row.emailAddress ?? row.email ?? "").trim() ? "F find email. " : ""}Esc back.` : "Up/Down scroll. Esc back.";
47417
47654
  if (state.tool?.name.startsWith("changes.")) help += " A approve. X reject. P apply. U restore. Each opens a preview.";
47418
47655
  if (state.tool?.name === "hygiene.duplicates") help += " M propose merge. X dismiss.";
47419
47656
  if (state.tool?.name === "hygiene.orphans") help += " P propose repair.";
@@ -47539,7 +47776,7 @@ function screenButtons(state) {
47539
47776
  if (state.tool?.name === "hygiene.duplicates") return [["Merge", "m"], ["Dismiss", "x"], ["Back", "escape"]];
47540
47777
  if (coreRecord(state.row)) {
47541
47778
  const row = detailRecord(state.row);
47542
- const emailAction = String(row.id).startsWith("hdp_") ? String(row.emailAddress ?? row.email ?? "").trim() ? [["Verify email", "v"]] : [["Find email", "f"]] : [];
47779
+ const emailAction = String(row.id).startsWith("hdp_") && !String(row.emailAddress ?? row.email ?? "").trim() ? [["Find email", "f"]] : [];
47543
47780
  return [["Add to list", "a"], ...emailAction, ["Suppression", "s"], ["Back", "escape"]];
47544
47781
  }
47545
47782
  }
@@ -47991,7 +48228,6 @@ function InteractiveApp({ session, fullScreen = true, events }) {
47991
48228
  } else if (isCore && input2 === "a") form("lists.add", { ids: [record2.id] });
47992
48229
  else if (isCore && String(record2.id).startsWith("hdp_") && !String(record2.emailAddress ?? record2.email ?? "").trim() && input2 === "f") form("email.find", { firstName: record2.firstName, lastName: record2.lastName, company: record2.companyId ? { id: record2.companyId } : {} });
47993
48230
  else if (isCore && input2 === "s") form("suppress.check", { keys: [{ keyType: String(record2.id).startsWith("hdo_") ? "company_id" : "person_id", value: record2.id }] });
47994
- else if (isCore && String(record2.id).startsWith("hdp_") && !!String(record2.emailAddress ?? record2.email ?? "").trim() && input2 === "v") form("email.verify", { email: record2.emailAddress ?? record2.email });
47995
48231
  }
47996
48232
  }
47997
48233
  useInput(handleInput);
@@ -48254,7 +48490,7 @@ var peopleArrayFlags = {
48254
48490
  excludeEmail: "excludeEmails"
48255
48491
  };
48256
48492
  var scalarFlags = ["employeeCountMin", "employeeCountMax", "revenueMinUsd", "revenueMaxUsd", "websiteAvailability", "linkedinAvailability", "fundingDateAfter", "fundingDateBefore"];
48257
- var peopleScalarFlags = ["companyFoundedYearMin", "companyFoundedYearMax", "jobStartDateAfter", "jobStartDateBefore", "emailAvailability", "phoneAvailability", "requireVerifiedEmail"];
48493
+ var peopleScalarFlags = ["sort", "companyFoundedYearMin", "companyFoundedYearMax", "jobStartDateAfter", "jobStartDateBefore", "emailAvailability", "phoneAvailability", "requireVerifiedEmail"];
48258
48494
  var flag = (name2) => name2.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`);
48259
48495
  var collect = (value, previous = []) => [...previous, value];
48260
48496
  function positiveLimit(value) {
@@ -48297,6 +48533,39 @@ async function buildFilters(api, options, kind) {
48297
48533
 
48298
48534
  // src/commands.ts
48299
48535
  init_output();
48536
+
48537
+ // src/guide.ts
48538
+ var AGENT_GUIDE = `HigherData agent guide
48539
+
48540
+ Count first, then download.
48541
+
48542
+ higherdata count --country-code CA --json
48543
+ That estimates companies and their contacts. For a people count:
48544
+ higherdata tools search.count_people --input '{"filters":{"countryCodes":["CA"]}}' --json
48545
+
48546
+ higherdata search people --country-code CA --title-keyword Operations --all --out people.jsonl
48547
+ --all requests the largest page the API allows (500) and writes every matching row.
48548
+ Progress prints "344 rows" while it runs and "453 rows (all)" when the download finishes.
48549
+ The 5,000 row cap is mentioned only when a download hits it. Save the search to a list and export the list for a larger set.
48550
+ --out people.jsonl has the same fields as --jsonl. A .csv file has those same fields. Do not download twice.
48551
+
48552
+ Filters and allowed values.
48553
+
48554
+ higherdata search people --help
48555
+ higherdata search companies --help
48556
+ higherdata tools search.facets --json
48557
+ Facets list country, state, industry, NAICS, funding, department, seniority and role family values, plus city suggestions.
48558
+ --where "Canadian software companies" proposes filters before the search.
48559
+ A people search needs a country or an industry.
48560
+
48561
+ Company lookup.
48562
+
48563
+ Do not call lookup.company once per person.
48564
+ When batch company lookup is available, pass up to 100 hdo_ ids in one lookup.company call.
48565
+ Until that call accepts ids, lookup.company takes one domain, name, LinkedIn URL, or hdo_ id.
48566
+ `;
48567
+
48568
+ // src/commands.ts
48300
48569
  init_search_download2();
48301
48570
 
48302
48571
  // src/pagination.ts
@@ -48366,11 +48635,11 @@ function wholeNumber(value) {
48366
48635
  }
48367
48636
  var collectScope = (value, previous = []) => [...previous, value];
48368
48637
  function registerAccountCommands(program2, deps) {
48369
- program2.command("usage").description("Show monthly activity and email verification allowance").action(async (_options, command) => {
48638
+ program2.command("usage").description("Show monthly activity and purchased verify credits").action(async (_options, command) => {
48370
48639
  const { api, mode } = await deps.session(command);
48371
48640
  deps.emit(await api.tool("account.usage", {}), mode);
48372
48641
  });
48373
- program2.command("limits").description("Show rate limits, the plan allowance, and the reset date").action(async (_options, command) => {
48642
+ program2.command("limits").description("Show plan rate limits, concurrency and purchased verify credits").action(async (_options, command) => {
48374
48643
  const { api, mode } = await deps.session(command);
48375
48644
  deps.emit(await api.tool("account.limits", {}), mode);
48376
48645
  });
@@ -48609,6 +48878,9 @@ function registerCommands(program2, runtime) {
48609
48878
  await runtime.writeConfig(config2);
48610
48879
  emit(identity, mode);
48611
48880
  });
48881
+ program2.command("guide").description("Print the agent quick reference").action(() => {
48882
+ runtime.out(AGENT_GUIDE);
48883
+ });
48612
48884
  const search = program2.command("search").description("Search HigherData Core");
48613
48885
  for (const kind of ["companies", "people"]) pageOptions(excludeList(addFilterOptions(search.command(kind).description(`Find ${kind}`), kind === "people"))).option("--out <path>", "Download to a new .csv or .jsonl file (up to 5,000 rows with --all)").action(async (options, command) => {
48614
48886
  const { api, mode } = await session(command);
@@ -48632,14 +48904,16 @@ function registerCommands(program2, runtime) {
48632
48904
  const { api, mode } = await session(command);
48633
48905
  emit(await api.tool("lookup.find", { query: query.join(" "), ...options.company ? { companyId: options.company } : {} }), mode);
48634
48906
  });
48635
- excludeList(lookup.command("company <domain>")).action(async (domain2, options, command) => {
48636
- const { api, mode } = await session(command);
48637
- emit(await api.tool("lookup.company", { domain: domain2, ...excluded(options) }), mode);
48638
- });
48639
- excludeList(lookup.command("person <email>")).action(async (email3, options, command) => {
48640
- const { api, mode } = await session(command);
48641
- emit(await api.tool("lookup.person", { email: email3, ...excluded(options) }), mode);
48642
- });
48907
+ for (const kind of ["company", "person"]) {
48908
+ excludeList(lookup.command(`${kind} [identity]`).option("--linkedin <url>", `Look up a ${kind} by LinkedIn URL`).option("--ids <ids>", "Company ids, comma-separated (up to 100)")).action(async (identity, options, command) => {
48909
+ if (Number(Boolean(identity)) + Number(Boolean(options.linkedin)) + Number(Boolean(options.ids)) !== 1) throw new CliError("Supply one identity, --linkedin URL or --ids list.");
48910
+ if (options.ids && kind !== "company") throw new CliError("--ids is available for company lookup only.");
48911
+ const ids = options.ids?.split(",").map((id) => id.trim());
48912
+ if (ids && (!ids.length || ids.length > 100 || ids.some((id) => !/^hdo_[0-9a-f]{32}$/.test(id)))) throw new CliError("Use 1 to 100 opaque hdo_ company ids.");
48913
+ const { api, mode } = await session(command);
48914
+ emit(await api.tool(`lookup.${kind}`, { ...ids ? { ids } : options.linkedin ? { linkedinUrl: options.linkedin } : kind === "company" ? { domain: identity } : { email: identity }, ...excluded(options) }), mode);
48915
+ });
48916
+ }
48643
48917
  const lists = program2.command("lists").description("Create, refresh and export saved lists");
48644
48918
  addFilterOptions(lists.command("create <name>").option("--kind <kind>", "companies or people", "companies").option("--recurring", "Refresh automatically on each data release").option("--dry-run", "Preview without creating a list"), true).action(async (name2, options, command) => {
48645
48919
  if (options.kind !== "companies" && options.kind !== "people") throw new CliError("Choose --kind companies or people.");
@@ -48712,7 +48986,7 @@ function createProgram(overrides = {}) {
48712
48986
  } else program2.outputHelp();
48713
48987
  });
48714
48988
  program2.hook("preAction", async (_program, command) => {
48715
- if (["login", "update", "upgrade"].includes(command.name()) || ["mcp", "skills", "config"].includes(command.parent?.name() ?? "")) return;
48989
+ if (["login", "update", "upgrade", "guide"].includes(command.name()) || ["mcp", "skills", "config"].includes(command.parent?.name() ?? "")) return;
48716
48990
  if (command === program2 && (!runtime.updater.stdinTTY || !runtime.updater.stdoutTTY || Object.hasOwn(runtime.env, "CI") || program2.opts().json || program2.opts().jsonl)) return;
48717
48991
  const settings = resolveSettings(await runtime.readConfig(), command.optsWithGlobals(), runtime.env);
48718
48992
  await checkCliVersion({ apiUrl: settings.apiUrl, version: package_default.version, cache: runtime.versionCache, fetchImpl: runtime.fetchImpl, now: runtime.now, warn: runtime.err });