openings 0.1.30 → 0.1.32

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openings",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Find evidence-grounded jobs, including relevant roles you may not have searched for, without accounts or API keys.",
5
5
  "author": { "name": "Openings contributors" },
6
6
  "license": "MIT",
@@ -969,7 +969,7 @@
969
969
  }
970
970
  },
971
971
  "3m": {
972
- "name": "3m",
972
+ "name": "3M",
973
973
  "ats": "workday",
974
974
  "token": "3m.wd1.myworkdayjobs.com/3m/Search",
975
975
  "sourceUrl": "https://3m.wd1.myworkdayjobs.com/en-US/Search",
@@ -978,7 +978,7 @@
978
978
  "reference": "https://index.commoncrawl.org/CC-MAIN-2026-30-index"
979
979
  },
980
980
  "verification": {
981
- "observedCompanyName": "3m",
981
+ "observedCompanyName": "3M",
982
982
  "identityEvidence": "provider_board",
983
983
  "contentType": "application/json",
984
984
  "payloadVersion": "workday-cxs:v1",
@@ -11034,7 +11034,7 @@
11034
11034
  }
11035
11035
  },
11036
11036
  "amat": {
11037
- "name": "Amat",
11037
+ "name": "Applied Materials",
11038
11038
  "ats": "workday",
11039
11039
  "token": "amat.wd1.myworkdayjobs.com/amat/External",
11040
11040
  "sourceUrl": "https://amat.wd1.myworkdayjobs.com/en-US/External",
@@ -11043,7 +11043,7 @@
11043
11043
  "reference": "https://index.commoncrawl.org/CC-MAIN-2026-30-index"
11044
11044
  },
11045
11045
  "verification": {
11046
- "observedCompanyName": "Amat",
11046
+ "observedCompanyName": "Applied Materials",
11047
11047
  "identityEvidence": "provider_board",
11048
11048
  "contentType": "application/json",
11049
11049
  "payloadVersion": "workday-cxs:v1",
@@ -23127,7 +23127,7 @@
23127
23127
  }
23128
23128
  },
23129
23129
  "bakertilly": {
23130
- "name": "Bakertilly",
23130
+ "name": "Baker Tilly",
23131
23131
  "ats": "workday",
23132
23132
  "token": "bakertilly.wd5.myworkdayjobs.com/bakertilly/BTCareers",
23133
23133
  "sourceUrl": "https://bakertilly.wd5.myworkdayjobs.com/en-US/BTCareers",
@@ -23136,7 +23136,7 @@
23136
23136
  "reference": "common-crawl-workday-host:bakertilly"
23137
23137
  },
23138
23138
  "verification": {
23139
- "observedCompanyName": "Bakertilly",
23139
+ "observedCompanyName": "Baker Tilly",
23140
23140
  "identityEvidence": "provider_board",
23141
23141
  "contentType": "application/json",
23142
23142
  "payloadVersion": "workday-cxs:v1",
@@ -36962,6 +36962,25 @@
36962
36962
  "canonicalSourceUrl": "https://sjinnovation.keka.com/careers"
36963
36963
  }
36964
36964
  },
36965
+ "careerbloc": {
36966
+ "name": "Career bloc",
36967
+ "ats": "smartrecruiters",
36968
+ "token": "careerbloc",
36969
+ "sourceUrl": "https://jobs.smartrecruiters.com/careerbloc",
36970
+ "discoveredFrom": {
36971
+ "channel": "search",
36972
+ "reference": "hiring-signal:Careerbloc"
36973
+ },
36974
+ "verification": {
36975
+ "observedCompanyName": "Career bloc",
36976
+ "identityEvidence": "provider_board",
36977
+ "contentType": "application/json; charset=utf-8",
36978
+ "payloadVersion": "smartrecruiters-postings:v1",
36979
+ "jobCount": 1,
36980
+ "checkedAt": "2026-09-11T06:50:14.702Z",
36981
+ "canonicalSourceUrl": "https://jobs.smartrecruiters.com/careerbloc"
36982
+ }
36983
+ },
36965
36984
  "careers": {
36966
36985
  "name": "Toddle",
36967
36986
  "ats": "keka",
@@ -42562,7 +42581,7 @@
42562
42581
  }
42563
42582
  },
42564
42583
  "cnx": {
42565
- "name": "Cnx",
42584
+ "name": "Concentrix",
42566
42585
  "ats": "workday",
42567
42586
  "token": "cnx.wd1.myworkdayjobs.com/cnx/external_global",
42568
42587
  "sourceUrl": "https://cnx.wd1.myworkdayjobs.com/en-US/external_global",
@@ -42571,7 +42590,7 @@
42571
42590
  "reference": "https://index.commoncrawl.org/CC-MAIN-2026-30-index"
42572
42591
  },
42573
42592
  "verification": {
42574
- "observedCompanyName": "Cnx",
42593
+ "observedCompanyName": "Concentrix",
42575
42594
  "identityEvidence": "provider_board",
42576
42595
  "contentType": "application/json",
42577
42596
  "payloadVersion": "workday-cxs:v1",
@@ -48203,7 +48222,7 @@
48203
48222
  }
48204
48223
  },
48205
48224
  "cw": {
48206
- "name": "Cw",
48225
+ "name": "Cushman & Wakefield",
48207
48226
  "ats": "workday",
48208
48227
  "token": "cw.wd1.myworkdayjobs.com/cw/external",
48209
48228
  "sourceUrl": "https://cw.wd1.myworkdayjobs.com/en-US/external",
@@ -48212,7 +48231,7 @@
48212
48231
  "reference": "https://index.commoncrawl.org/CC-MAIN-2026-30-index"
48213
48232
  },
48214
48233
  "verification": {
48215
- "observedCompanyName": "Cw",
48234
+ "observedCompanyName": "Cushman & Wakefield",
48216
48235
  "identityEvidence": "provider_board",
48217
48236
  "contentType": "application/json",
48218
48237
  "payloadVersion": "workday-cxs:v1",
@@ -49656,7 +49675,7 @@
49656
49675
  }
49657
49676
  },
49658
49677
  "db": {
49659
- "name": "Db",
49678
+ "name": "Deutsche Bank",
49660
49679
  "ats": "workday",
49661
49680
  "token": "db.wd3.myworkdayjobs.com/db/DBWebsite",
49662
49681
  "sourceUrl": "https://db.wd3.myworkdayjobs.com/en-US/DBWebsite",
@@ -49665,7 +49684,7 @@
49665
49684
  "reference": "common-crawl-workday-host:db"
49666
49685
  },
49667
49686
  "verification": {
49668
- "observedCompanyName": "Db",
49687
+ "observedCompanyName": "Deutsche Bank",
49669
49688
  "identityEvidence": "provider_board",
49670
49689
  "contentType": "application/json",
49671
49690
  "payloadVersion": "workday-cxs:v1",
@@ -50591,7 +50610,7 @@
50591
50610
  }
50592
50611
  },
50593
50612
  "dentsuaegis": {
50594
- "name": "Dentsuaegis",
50613
+ "name": "Dentsu",
50595
50614
  "ats": "workday",
50596
50615
  "token": "dentsuaegis.wd3.myworkdayjobs.com/dentsuaegis/DAN_GLOBAL",
50597
50616
  "sourceUrl": "https://dentsuaegis.wd3.myworkdayjobs.com/en-US/DAN_GLOBAL",
@@ -50600,7 +50619,7 @@
50600
50619
  "reference": "common-crawl-workday-host:dentsuaegis"
50601
50620
  },
50602
50621
  "verification": {
50603
- "observedCompanyName": "Dentsuaegis",
50622
+ "observedCompanyName": "Dentsu",
50604
50623
  "identityEvidence": "provider_board",
50605
50624
  "contentType": "application/json",
50606
50625
  "payloadVersion": "workday-cxs:v1",
@@ -53024,7 +53043,7 @@
53024
53043
  }
53025
53044
  },
53026
53045
  "dxctechnology": {
53027
- "name": "Dxctechnology",
53046
+ "name": "DXC Technology",
53028
53047
  "ats": "workday",
53029
53048
  "token": "dxctechnology.wd1.myworkdayjobs.com/dxctechnology/DXCJobs",
53030
53049
  "sourceUrl": "https://dxctechnology.wd1.myworkdayjobs.com/en-US/DXCJobs",
@@ -53033,7 +53052,7 @@
53033
53052
  "reference": "https://index.commoncrawl.org/CC-MAIN-2026-30-index"
53034
53053
  },
53035
53054
  "verification": {
53036
- "observedCompanyName": "Dxctechnology",
53055
+ "observedCompanyName": "DXC Technology",
53037
53056
  "identityEvidence": "provider_board",
53038
53057
  "contentType": "application/json",
53039
53058
  "payloadVersion": "workday-cxs:v1",
@@ -54373,6 +54392,25 @@
54373
54392
  "canonicalSourceUrl": "https://jobs.ashbyhq.com/elevenlabs"
54374
54393
  }
54375
54394
  },
54395
+ "elfbeauty": {
54396
+ "name": "Elfbeauty",
54397
+ "ats": "lever",
54398
+ "token": "elfbeauty",
54399
+ "sourceUrl": "https://jobs.lever.co/elfbeauty",
54400
+ "discoveredFrom": {
54401
+ "channel": "search",
54402
+ "reference": "hiring-signal:Elf Beauty"
54403
+ },
54404
+ "verification": {
54405
+ "observedCompanyName": "Elfbeauty",
54406
+ "identityEvidence": "provider_board",
54407
+ "contentType": "application/json; charset=utf-8",
54408
+ "payloadVersion": "lever-postings:v0",
54409
+ "jobCount": 78,
54410
+ "checkedAt": "2026-09-11T06:50:14.584Z",
54411
+ "canonicalSourceUrl": "https://jobs.lever.co/elfbeauty"
54412
+ }
54413
+ },
54376
54414
  "eli": {
54377
54415
  "name": "Eli",
54378
54416
  "ats": "ashby",
@@ -63184,7 +63222,7 @@
63184
63222
  }
63185
63223
  },
63186
63224
  "gehc": {
63187
- "name": "Gehc",
63225
+ "name": "GE HealthCare",
63188
63226
  "ats": "workday",
63189
63227
  "token": "gehc.wd5.myworkdayjobs.com/gehc/GEHC_ExternalSite",
63190
63228
  "sourceUrl": "https://gehc.wd5.myworkdayjobs.com/en-US/GEHC_ExternalSite",
@@ -63193,7 +63231,7 @@
63193
63231
  "reference": "common-crawl-workday-host:gehc"
63194
63232
  },
63195
63233
  "verification": {
63196
- "observedCompanyName": "Gehc",
63234
+ "observedCompanyName": "GE HealthCare",
63197
63235
  "identityEvidence": "provider_board",
63198
63236
  "contentType": "application/json",
63199
63237
  "payloadVersion": "workday-cxs:v1",
@@ -64130,7 +64168,7 @@
64130
64168
  }
64131
64169
  },
64132
64170
  "gevernova": {
64133
- "name": "Gevernova",
64171
+ "name": "GE Vernova",
64134
64172
  "ats": "workday",
64135
64173
  "token": "gevernova.wd5.myworkdayjobs.com/gevernova/Vernova_ExternalSite",
64136
64174
  "sourceUrl": "https://gevernova.wd5.myworkdayjobs.com/en-US/Vernova_ExternalSite",
@@ -64139,7 +64177,7 @@
64139
64177
  "reference": "common-crawl-workday-host:gevernova"
64140
64178
  },
64141
64179
  "verification": {
64142
- "observedCompanyName": "Gevernova",
64180
+ "observedCompanyName": "GE Vernova",
64143
64181
  "identityEvidence": "provider_board",
64144
64182
  "contentType": "application/json",
64145
64183
  "payloadVersion": "workday-cxs:v1",
@@ -64841,7 +64879,7 @@
64841
64879
  }
64842
64880
  },
64843
64881
  "globalfoundries": {
64844
- "name": "Globalfoundries",
64882
+ "name": "GlobalFoundries",
64845
64883
  "ats": "workday",
64846
64884
  "token": "globalfoundries.wd1.myworkdayjobs.com/globalfoundries/External",
64847
64885
  "sourceUrl": "https://globalfoundries.wd1.myworkdayjobs.com/en-US/External",
@@ -64850,7 +64888,7 @@
64850
64888
  "reference": "common-crawl-workday-host:globalfoundries"
64851
64889
  },
64852
64890
  "verification": {
64853
- "observedCompanyName": "Globalfoundries",
64891
+ "observedCompanyName": "GlobalFoundries",
64854
64892
  "identityEvidence": "provider_board",
64855
64893
  "contentType": "application/json",
64856
64894
  "payloadVersion": "workday-cxs:v1",
@@ -64860,7 +64898,7 @@
64860
64898
  }
64861
64899
  },
64862
64900
  "globalhr": {
64863
- "name": "Globalhr",
64901
+ "name": "RTX",
64864
64902
  "ats": "workday",
64865
64903
  "token": "globalhr.wd5.myworkdayjobs.com/globalhr/REC_RTX_Ext_Gateway",
64866
64904
  "sourceUrl": "https://globalhr.wd5.myworkdayjobs.com/en-US/REC_RTX_Ext_Gateway",
@@ -64869,7 +64907,7 @@
64869
64907
  "reference": "common-crawl-workday-host:globalhr"
64870
64908
  },
64871
64909
  "verification": {
64872
- "observedCompanyName": "Globalhr",
64910
+ "observedCompanyName": "RTX",
64873
64911
  "identityEvidence": "provider_board",
64874
64912
  "contentType": "application/json",
64875
64913
  "payloadVersion": "workday-cxs:v1",
@@ -69191,7 +69229,7 @@
69191
69229
  }
69192
69230
  },
69193
69231
  "hcmportal": {
69194
- "name": "Hcmportal",
69232
+ "name": "UPS",
69195
69233
  "ats": "workday",
69196
69234
  "token": "hcmportal.wd5.myworkdayjobs.com/hcmportal/Search",
69197
69235
  "sourceUrl": "https://hcmportal.wd5.myworkdayjobs.com/en-US/Search",
@@ -69200,7 +69238,7 @@
69200
69238
  "reference": "common-crawl-workday-host:hcmportal"
69201
69239
  },
69202
69240
  "verification": {
69203
- "observedCompanyName": "Hcmportal",
69241
+ "observedCompanyName": "UPS",
69204
69242
  "identityEvidence": "provider_board",
69205
69243
  "contentType": "application/json",
69206
69244
  "payloadVersion": "workday-cxs:v1",
@@ -69752,7 +69790,7 @@
69752
69790
  }
69753
69791
  },
69754
69792
  "heinz": {
69755
- "name": "Heinz",
69793
+ "name": "Kraft Heinz",
69756
69794
  "ats": "workday",
69757
69795
  "token": "heinz.wd1.myworkdayjobs.com/heinz/KraftHeinz_Careers",
69758
69796
  "sourceUrl": "https://heinz.wd1.myworkdayjobs.com/en-US/KraftHeinz_Careers",
@@ -69761,7 +69799,7 @@
69761
69799
  "reference": "common-crawl-workday-host:heinz"
69762
69800
  },
69763
69801
  "verification": {
69764
- "observedCompanyName": "Heinz",
69802
+ "observedCompanyName": "Kraft Heinz",
69765
69803
  "identityEvidence": "provider_board",
69766
69804
  "contentType": "application/json",
69767
69805
  "payloadVersion": "workday-cxs:v1",
@@ -72326,7 +72364,7 @@
72326
72364
  }
72327
72365
  },
72328
72366
  "hp": {
72329
- "name": "Hp",
72367
+ "name": "HP",
72330
72368
  "ats": "workday",
72331
72369
  "token": "hp.wd5.myworkdayjobs.com/hp/ExternalCareerSite",
72332
72370
  "sourceUrl": "https://hp.wd5.myworkdayjobs.com/en-US/ExternalCareerSite",
@@ -72335,7 +72373,7 @@
72335
72373
  "reference": "common-crawl-workday-host:hp"
72336
72374
  },
72337
72375
  "verification": {
72338
- "observedCompanyName": "Hp",
72376
+ "observedCompanyName": "HP",
72339
72377
  "identityEvidence": "provider_board",
72340
72378
  "contentType": "application/json",
72341
72379
  "payloadVersion": "workday-cxs:v1",
@@ -72364,7 +72402,7 @@
72364
72402
  }
72365
72403
  },
72366
72404
  "hpe": {
72367
- "name": "Hpe",
72405
+ "name": "Hewlett Packard Enterprise",
72368
72406
  "ats": "workday",
72369
72407
  "token": "hpe.wd5.myworkdayjobs.com/hpe/Jobsathpe",
72370
72408
  "sourceUrl": "https://hpe.wd5.myworkdayjobs.com/en-US/Jobsathpe",
@@ -72373,7 +72411,7 @@
72373
72411
  "reference": "common-crawl-workday-host:hpe"
72374
72412
  },
72375
72413
  "verification": {
72376
- "observedCompanyName": "Hpe",
72414
+ "observedCompanyName": "Hewlett Packard Enterprise",
72377
72415
  "identityEvidence": "provider_board",
72378
72416
  "contentType": "application/json",
72379
72417
  "payloadVersion": "workday-cxs:v1",
@@ -76363,7 +76401,7 @@
76363
76401
  }
76364
76402
  },
76365
76403
  "ingrammicro": {
76366
- "name": "Ingrammicro",
76404
+ "name": "Ingram Micro",
76367
76405
  "ats": "workday",
76368
76406
  "token": "ingrammicro.wd5.myworkdayjobs.com/ingrammicro/IngramMicro",
76369
76407
  "sourceUrl": "https://ingrammicro.wd5.myworkdayjobs.com/en-US/IngramMicro",
@@ -76372,7 +76410,7 @@
76372
76410
  "reference": "common-crawl-workday-host:ingrammicro"
76373
76411
  },
76374
76412
  "verification": {
76375
- "observedCompanyName": "Ingrammicro",
76413
+ "observedCompanyName": "Ingram Micro",
76376
76414
  "identityEvidence": "provider_board",
76377
76415
  "contentType": "application/json",
76378
76416
  "payloadVersion": "workday-cxs:v1",
@@ -77684,6 +77722,25 @@
77684
77722
  "canonicalSourceUrl": "https://apply.workable.com/interpath-advisory/"
77685
77723
  }
77686
77724
  },
77725
+ "interviewkickstart": {
77726
+ "name": "Interview Kickstart",
77727
+ "ats": "smartrecruiters",
77728
+ "token": "interviewkickstart",
77729
+ "sourceUrl": "https://jobs.smartrecruiters.com/interviewkickstart",
77730
+ "discoveredFrom": {
77731
+ "channel": "search",
77732
+ "reference": "hiring-signal:Interview Kickstart"
77733
+ },
77734
+ "verification": {
77735
+ "observedCompanyName": "Interview Kickstart",
77736
+ "identityEvidence": "provider_board",
77737
+ "contentType": "application/json; charset=utf-8",
77738
+ "payloadVersion": "smartrecruiters-postings:v1",
77739
+ "jobCount": 3,
77740
+ "checkedAt": "2026-09-11T06:50:14.703Z",
77741
+ "canonicalSourceUrl": "https://jobs.smartrecruiters.com/interviewkickstart"
77742
+ }
77743
+ },
77687
77744
  "interweave": {
77688
77745
  "name": "Interweave",
77689
77746
  "ats": "workable",
@@ -79242,7 +79299,7 @@
79242
79299
  }
79243
79300
  },
79244
79301
  "jci": {
79245
- "name": "Jci",
79302
+ "name": "Johnson Controls",
79246
79303
  "ats": "workday",
79247
79304
  "token": "jci.wd5.myworkdayjobs.com/jci/JCI",
79248
79305
  "sourceUrl": "https://jci.wd5.myworkdayjobs.com/en-US/JCI",
@@ -79251,7 +79308,7 @@
79251
79308
  "reference": "common-crawl-workday-host:jci"
79252
79309
  },
79253
79310
  "verification": {
79254
- "observedCompanyName": "Jci",
79311
+ "observedCompanyName": "Johnson Controls",
79255
79312
  "identityEvidence": "provider_board",
79256
79313
  "contentType": "application/json",
79257
79314
  "payloadVersion": "workday-cxs:v1",
@@ -79394,7 +79451,7 @@
79394
79451
  }
79395
79452
  },
79396
79453
  "jj": {
79397
- "name": "Jj",
79454
+ "name": "Johnson & Johnson",
79398
79455
  "ats": "workday",
79399
79456
  "token": "jj.wd5.myworkdayjobs.com/jj/JJ",
79400
79457
  "sourceUrl": "https://jj.wd5.myworkdayjobs.com/en-US/JJ",
@@ -79403,7 +79460,7 @@
79403
79460
  "reference": "common-crawl-workday-host:jj"
79404
79461
  },
79405
79462
  "verification": {
79406
- "observedCompanyName": "Jj",
79463
+ "observedCompanyName": "Johnson & Johnson",
79407
79464
  "identityEvidence": "provider_board",
79408
79465
  "contentType": "application/json",
79409
79466
  "payloadVersion": "workday-cxs:v1",
@@ -79511,6 +79568,25 @@
79511
79568
  "canonicalSourceUrl": "https://jnd-inc.breezy.hr"
79512
79569
  }
79513
79570
  },
79571
+ "jobgether": {
79572
+ "name": "Jobgether",
79573
+ "ats": "lever",
79574
+ "token": "jobgether",
79575
+ "sourceUrl": "https://jobs.lever.co/jobgether",
79576
+ "discoveredFrom": {
79577
+ "channel": "search",
79578
+ "reference": "hiring-signal:Jobgether"
79579
+ },
79580
+ "verification": {
79581
+ "observedCompanyName": "Jobgether",
79582
+ "identityEvidence": "provider_board",
79583
+ "contentType": "application/json; charset=utf-8",
79584
+ "payloadVersion": "lever-postings:v0",
79585
+ "jobCount": 4324,
79586
+ "checkedAt": "2026-09-11T06:50:14.610Z",
79587
+ "canonicalSourceUrl": "https://jobs.lever.co/jobgether"
79588
+ }
79589
+ },
79514
79590
  "jobhire": {
79515
79591
  "name": "Jobhire",
79516
79592
  "ats": "workable",
@@ -83651,6 +83727,25 @@
83651
83727
  "canonicalSourceUrl": "https://apply.workable.com/levelagency/"
83652
83728
  }
83653
83729
  },
83730
+ "levelai": {
83731
+ "name": "Levelai",
83732
+ "ats": "lever",
83733
+ "token": "levelai",
83734
+ "sourceUrl": "https://jobs.lever.co/levelai",
83735
+ "discoveredFrom": {
83736
+ "channel": "search",
83737
+ "reference": "hiring-signal:Level AI"
83738
+ },
83739
+ "verification": {
83740
+ "observedCompanyName": "Levelai",
83741
+ "identityEvidence": "provider_board",
83742
+ "contentType": "application/json; charset=utf-8",
83743
+ "payloadVersion": "lever-postings:v0",
83744
+ "jobCount": 19,
83745
+ "checkedAt": "2026-09-11T06:50:14.626Z",
83746
+ "canonicalSourceUrl": "https://jobs.lever.co/levelai"
83747
+ }
83748
+ },
83654
83749
  "levelall": {
83655
83750
  "name": "Level All",
83656
83751
  "ats": "smartrecruiters",
@@ -85105,7 +85200,7 @@
85105
85200
  }
85106
85201
  },
85107
85202
  "lseg": {
85108
- "name": "Lseg",
85203
+ "name": "LSEG",
85109
85204
  "ats": "workday",
85110
85205
  "token": "lseg.wd3.myworkdayjobs.com/lseg/Careers",
85111
85206
  "sourceUrl": "https://lseg.wd3.myworkdayjobs.com/en-US/Careers",
@@ -85114,7 +85209,7 @@
85114
85209
  "reference": "common-crawl-workday-host:lseg"
85115
85210
  },
85116
85211
  "verification": {
85117
- "observedCompanyName": "Lseg",
85212
+ "observedCompanyName": "LSEG",
85118
85213
  "identityEvidence": "provider_board",
85119
85214
  "contentType": "application/json",
85120
85215
  "payloadVersion": "workday-cxs:v1",
@@ -88901,7 +88996,7 @@
88901
88996
  }
88902
88997
  },
88903
88998
  "ms": {
88904
- "name": "Ms",
88999
+ "name": "Morgan Stanley",
88905
89000
  "ats": "workday",
88906
89001
  "token": "ms.wd5.myworkdayjobs.com/ms/External",
88907
89002
  "sourceUrl": "https://ms.wd5.myworkdayjobs.com/en-US/External",
@@ -88910,7 +89005,7 @@
88910
89005
  "reference": "common-crawl-workday-host:ms"
88911
89006
  },
88912
89007
  "verification": {
88913
- "observedCompanyName": "Ms",
89008
+ "observedCompanyName": "Morgan Stanley",
88914
89009
  "identityEvidence": "provider_board",
88915
89010
  "contentType": "application/json",
88916
89011
  "payloadVersion": "workday-cxs:v1",
@@ -89091,7 +89186,7 @@
89091
89186
  }
89092
89187
  },
89093
89188
  "mufgub": {
89094
- "name": "Mufgub",
89189
+ "name": "MUFG Bank",
89095
89190
  "ats": "workday",
89096
89191
  "token": "mufgub.wd3.myworkdayjobs.com/mufgub/MUFG-Careers",
89097
89192
  "sourceUrl": "https://mufgub.wd3.myworkdayjobs.com/en-US/MUFG-Careers",
@@ -89100,7 +89195,7 @@
89100
89195
  "reference": "common-crawl-workday-host:mufgub"
89101
89196
  },
89102
89197
  "verification": {
89103
- "observedCompanyName": "Mufgub",
89198
+ "observedCompanyName": "MUFG Bank",
89104
89199
  "identityEvidence": "provider_board",
89105
89200
  "contentType": "application/json",
89106
89201
  "payloadVersion": "workday-cxs:v1",
@@ -89448,7 +89543,7 @@
89448
89543
  }
89449
89544
  },
89450
89545
  "myhrabc": {
89451
- "name": "Myhrabc",
89546
+ "name": "Cencora",
89452
89547
  "ats": "workday",
89453
89548
  "token": "myhrabc.wd5.myworkdayjobs.com/myhrabc/Global",
89454
89549
  "sourceUrl": "https://myhrabc.wd5.myworkdayjobs.com/en-US/Global",
@@ -89457,7 +89552,7 @@
89457
89552
  "reference": "common-crawl-workday-host:myhrabc"
89458
89553
  },
89459
89554
  "verification": {
89460
- "observedCompanyName": "Myhrabc",
89555
+ "observedCompanyName": "Cencora",
89461
89556
  "identityEvidence": "provider_board",
89462
89557
  "contentType": "application/json",
89463
89558
  "payloadVersion": "workday-cxs:v1",
@@ -89600,7 +89695,7 @@
89600
89695
  }
89601
89696
  },
89602
89697
  "nab": {
89603
- "name": "Nab",
89698
+ "name": "National Australia Bank",
89604
89699
  "ats": "workday",
89605
89700
  "token": "nab.wd3.myworkdayjobs.com/nab/nab_careers",
89606
89701
  "sourceUrl": "https://nab.wd3.myworkdayjobs.com/en-US/nab_careers",
@@ -89609,7 +89704,7 @@
89609
89704
  "reference": "common-crawl-workday-host:nab"
89610
89705
  },
89611
89706
  "verification": {
89612
- "observedCompanyName": "Nab",
89707
+ "observedCompanyName": "National Australia Bank",
89613
89708
  "identityEvidence": "provider_board",
89614
89709
  "contentType": "application/json",
89615
89710
  "payloadVersion": "workday-cxs:v1",
@@ -89698,6 +89793,25 @@
89698
89793
  "canonicalSourceUrl": "https://jobs.smartrecruiters.com/Nanoheal1"
89699
89794
  }
89700
89795
  },
89796
+ "nanonets": {
89797
+ "name": "Nanonets",
89798
+ "ats": "ashby",
89799
+ "token": "nanonets",
89800
+ "sourceUrl": "https://jobs.ashbyhq.com/nanonets",
89801
+ "discoveredFrom": {
89802
+ "channel": "search",
89803
+ "reference": "hiring-signal:Nanonets"
89804
+ },
89805
+ "verification": {
89806
+ "observedCompanyName": "Nanonets",
89807
+ "identityEvidence": "provider_board",
89808
+ "contentType": "application/json; charset=utf-8",
89809
+ "payloadVersion": "ashby-job-board:1",
89810
+ "jobCount": 9,
89811
+ "checkedAt": "2026-09-11T06:50:14.392Z",
89812
+ "canonicalSourceUrl": "https://jobs.ashbyhq.com/nanonets"
89813
+ }
89814
+ },
89701
89815
  "napaautoparts33": {
89702
89816
  "name": "NAPA Auto Parts",
89703
89817
  "ats": "smartrecruiters",
@@ -91251,7 +91365,7 @@
91251
91365
  }
91252
91366
  },
91253
91367
  "ntrs": {
91254
- "name": "Ntrs",
91368
+ "name": "Northern Trust",
91255
91369
  "ats": "workday",
91256
91370
  "token": "ntrs.wd1.myworkdayjobs.com/ntrs/northerntrust",
91257
91371
  "sourceUrl": "https://ntrs.wd1.myworkdayjobs.com/en-US/northerntrust",
@@ -91260,7 +91374,7 @@
91260
91374
  "reference": "common-crawl-workday-host:ntrs"
91261
91375
  },
91262
91376
  "verification": {
91263
- "observedCompanyName": "Ntrs",
91377
+ "observedCompanyName": "Northern Trust",
91264
91378
  "identityEvidence": "provider_board",
91265
91379
  "contentType": "application/json",
91266
91380
  "payloadVersion": "workday-cxs:v1",
@@ -91312,6 +91426,25 @@
91312
91426
  "canonicalSourceUrl": "https://zanskar.keka.com/careers"
91313
91427
  }
91314
91428
  },
91429
+ "numeric": {
91430
+ "name": "Numeric",
91431
+ "ats": "ashby",
91432
+ "token": "numeric",
91433
+ "sourceUrl": "https://jobs.ashbyhq.com/numeric",
91434
+ "discoveredFrom": {
91435
+ "channel": "search",
91436
+ "reference": "hiring-signal:Numeric Technologies Incorporation"
91437
+ },
91438
+ "verification": {
91439
+ "observedCompanyName": "Numeric",
91440
+ "identityEvidence": "provider_board",
91441
+ "contentType": "application/json; charset=utf-8",
91442
+ "payloadVersion": "ashby-job-board:1",
91443
+ "jobCount": 16,
91444
+ "checkedAt": "2026-09-11T06:50:14.392Z",
91445
+ "canonicalSourceUrl": "https://jobs.ashbyhq.com/numeric"
91446
+ }
91447
+ },
91315
91448
  "nurix": {
91316
91449
  "name": "Nurix",
91317
91450
  "ats": "keka",
@@ -91441,7 +91574,7 @@
91441
91574
  }
91442
91575
  },
91443
91576
  "nxp": {
91444
- "name": "Nxp",
91577
+ "name": "NXP Semiconductors",
91445
91578
  "ats": "workday",
91446
91579
  "token": "nxp.wd3.myworkdayjobs.com/nxp/careers",
91447
91580
  "sourceUrl": "https://nxp.wd3.myworkdayjobs.com/en-US/careers",
@@ -91450,7 +91583,7 @@
91450
91583
  "reference": "common-crawl-workday-host:nxp"
91451
91584
  },
91452
91585
  "verification": {
91453
- "observedCompanyName": "Nxp",
91586
+ "observedCompanyName": "NXP Semiconductors",
91454
91587
  "identityEvidence": "provider_board",
91455
91588
  "contentType": "application/json",
91456
91589
  "payloadVersion": "workday-cxs:v1",
@@ -91845,7 +91978,7 @@
91845
91978
  }
91846
91979
  },
91847
91980
  "onetp": {
91848
- "name": "Onetp",
91981
+ "name": "Teleperformance",
91849
91982
  "ats": "workday",
91850
91983
  "token": "onetp.wd1.myworkdayjobs.com/onetp/Teleperformance",
91851
91984
  "sourceUrl": "https://onetp.wd1.myworkdayjobs.com/en-US/Teleperformance",
@@ -91854,7 +91987,7 @@
91854
91987
  "reference": "common-crawl-workday-host:onetp"
91855
91988
  },
91856
91989
  "verification": {
91857
- "observedCompanyName": "Onetp",
91990
+ "observedCompanyName": "Teleperformance",
91858
91991
  "identityEvidence": "provider_board",
91859
91992
  "contentType": "application/json",
91860
91993
  "payloadVersion": "workday-cxs:v1",
@@ -92550,6 +92683,25 @@
92550
92683
  "canonicalSourceUrl": "https://jobs.lever.co/paytm"
92551
92684
  }
92552
92685
  },
92686
+ "paytmpayments": {
92687
+ "name": "Paytmpayments",
92688
+ "ats": "lever",
92689
+ "token": "paytmpayments",
92690
+ "sourceUrl": "https://jobs.lever.co/paytmpayments",
92691
+ "discoveredFrom": {
92692
+ "channel": "search",
92693
+ "reference": "hiring-signal:Paytm Payments Services"
92694
+ },
92695
+ "verification": {
92696
+ "observedCompanyName": "Paytmpayments",
92697
+ "identityEvidence": "provider_board",
92698
+ "contentType": "application/json; charset=utf-8",
92699
+ "payloadVersion": "lever-postings:v0",
92700
+ "jobCount": 921,
92701
+ "checkedAt": "2026-09-11T06:50:14.691Z",
92702
+ "canonicalSourceUrl": "https://jobs.lever.co/paytmpayments"
92703
+ }
92704
+ },
92553
92705
  "pbjcal": {
92554
92706
  "name": "Pbjcal",
92555
92707
  "ats": "workday",
@@ -94118,7 +94270,7 @@
94118
94270
  }
94119
94271
  },
94120
94272
  "pwc": {
94121
- "name": "Pwc",
94273
+ "name": "PwC",
94122
94274
  "ats": "workday",
94123
94275
  "token": "pwc.wd3.myworkdayjobs.com/pwc/Global_Experienced_Careers",
94124
94276
  "sourceUrl": "https://pwc.wd3.myworkdayjobs.com/en-US/Global_Experienced_Careers",
@@ -94127,7 +94279,7 @@
94127
94279
  "reference": "common-crawl-workday-host:pwc"
94128
94280
  },
94129
94281
  "verification": {
94130
- "observedCompanyName": "Pwc",
94282
+ "observedCompanyName": "PwC",
94131
94283
  "identityEvidence": "provider_board",
94132
94284
  "contentType": "application/json",
94133
94285
  "payloadVersion": "workday-cxs:v1",
@@ -94489,7 +94641,7 @@
94489
94641
  }
94490
94642
  },
94491
94643
  "r1rcm": {
94492
- "name": "R1rcm",
94644
+ "name": "R1 RCM",
94493
94645
  "ats": "workday",
94494
94646
  "token": "r1rcm.wd1.myworkdayjobs.com/r1rcm/R1RCM",
94495
94647
  "sourceUrl": "https://r1rcm.wd1.myworkdayjobs.com/en-US/R1RCM",
@@ -94498,7 +94650,7 @@
94498
94650
  "reference": "common-crawl-workday-host:r1rcm"
94499
94651
  },
94500
94652
  "verification": {
94501
- "observedCompanyName": "R1rcm",
94653
+ "observedCompanyName": "R1 RCM",
94502
94654
  "identityEvidence": "provider_board",
94503
94655
  "contentType": "application/json",
94504
94656
  "payloadVersion": "workday-cxs:v1",
@@ -95727,7 +95879,7 @@
95727
95879
  }
95728
95880
  },
95729
95881
  "rsm": {
95730
- "name": "Rsm",
95882
+ "name": "RSM",
95731
95883
  "ats": "workday",
95732
95884
  "token": "rsm.wd1.myworkdayjobs.com/rsm/RSMCareers",
95733
95885
  "sourceUrl": "https://rsm.wd1.myworkdayjobs.com/en-US/RSMCareers",
@@ -95736,7 +95888,7 @@
95736
95888
  "reference": "common-crawl-workday-host:RSM US LLP"
95737
95889
  },
95738
95890
  "verification": {
95739
- "observedCompanyName": "Rsm",
95891
+ "observedCompanyName": "RSM",
95740
95892
  "identityEvidence": "provider_board",
95741
95893
  "contentType": "application/json",
95742
95894
  "payloadVersion": "workday-cxs:v1",
@@ -97898,7 +98050,7 @@
97898
98050
  }
97899
98051
  },
97900
98052
  "slihrms": {
97901
- "name": "Slihrms",
98053
+ "name": "AtkinsR\u00e9alis",
97902
98054
  "ats": "workday",
97903
98055
  "token": "slihrms.wd3.myworkdayjobs.com/slihrms/Careers",
97904
98056
  "sourceUrl": "https://slihrms.wd3.myworkdayjobs.com/en-US/Careers",
@@ -97907,7 +98059,7 @@
97907
98059
  "reference": "common-crawl-workday-host:slihrms"
97908
98060
  },
97909
98061
  "verification": {
97910
- "observedCompanyName": "Slihrms",
98062
+ "observedCompanyName": "AtkinsR\u00e9alis",
97911
98063
  "identityEvidence": "provider_board",
97912
98064
  "contentType": "application/json",
97913
98065
  "payloadVersion": "workday-cxs:v1",
@@ -98802,6 +98954,25 @@
98802
98954
  "canonicalSourceUrl": "https://jobs.smartrecruiters.com/Sportradar"
98803
98955
  }
98804
98956
  },
98957
+ "spotdraft": {
98958
+ "name": "Spotdraft",
98959
+ "ats": "ashby",
98960
+ "token": "spotdraft",
98961
+ "sourceUrl": "https://jobs.ashbyhq.com/spotdraft",
98962
+ "discoveredFrom": {
98963
+ "channel": "search",
98964
+ "reference": "hiring-signal:SpotDraft"
98965
+ },
98966
+ "verification": {
98967
+ "observedCompanyName": "Spotdraft",
98968
+ "identityEvidence": "provider_board",
98969
+ "contentType": "application/json; charset=utf-8",
98970
+ "payloadVersion": "ashby-job-board:1",
98971
+ "jobCount": 13,
98972
+ "checkedAt": "2026-09-11T06:50:14.392Z",
98973
+ "canonicalSourceUrl": "https://jobs.ashbyhq.com/spotdraft"
98974
+ }
98975
+ },
98805
98976
  "springernature": {
98806
98977
  "name": "Springernature",
98807
98978
  "ats": "workday",
@@ -99819,7 +99990,7 @@
99819
99990
  }
99820
99991
  },
99821
99992
  "syneoshealth": {
99822
- "name": "Syneoshealth",
99993
+ "name": "Syneos Health",
99823
99994
  "ats": "workday",
99824
99995
  "token": "syneoshealth.wd12.myworkdayjobs.com/syneoshealth/Syneos_Health_External_Site",
99825
99996
  "sourceUrl": "https://syneoshealth.wd12.myworkdayjobs.com/en-US/Syneos_Health_External_Site",
@@ -99828,7 +99999,7 @@
99828
99999
  "reference": "common-crawl-workday-host:syneoshealth"
99829
100000
  },
99830
100001
  "verification": {
99831
- "observedCompanyName": "Syneoshealth",
100002
+ "observedCompanyName": "Syneos Health",
99832
100003
  "identityEvidence": "provider_board",
99833
100004
  "contentType": "application/json",
99834
100005
  "payloadVersion": "workday-cxs:v1",
@@ -100141,7 +100312,7 @@
100141
100312
  }
100142
100313
  },
100143
100314
  "taskus": {
100144
- "name": "Taskus",
100315
+ "name": "TaskUs",
100145
100316
  "ats": "workday",
100146
100317
  "token": "taskus.wd1.myworkdayjobs.com/taskus/Careers",
100147
100318
  "sourceUrl": "https://taskus.wd1.myworkdayjobs.com/en-US/Careers",
@@ -100150,7 +100321,7 @@
100150
100321
  "reference": "common-crawl-workday-host:taskus"
100151
100322
  },
100152
100323
  "verification": {
100153
- "observedCompanyName": "Taskus",
100324
+ "observedCompanyName": "TaskUs",
100154
100325
  "identityEvidence": "provider_board",
100155
100326
  "contentType": "application/json",
100156
100327
  "payloadVersion": "workday-cxs:v1",
@@ -101783,7 +101954,7 @@
101783
101954
  }
101784
101955
  },
101785
101956
  "tranetechnologies": {
101786
- "name": "Tranetechnologies",
101957
+ "name": "Trane Technologies",
101787
101958
  "ats": "workday",
101788
101959
  "token": "tranetechnologies.wd12.myworkdayjobs.com/tranetechnologies/Trane_Technologies_Careers",
101789
101960
  "sourceUrl": "https://tranetechnologies.wd12.myworkdayjobs.com/en-US/Trane_Technologies_Careers",
@@ -101792,7 +101963,7 @@
101792
101963
  "reference": "common-crawl-workday-host:tranetechnologies"
101793
101964
  },
101794
101965
  "verification": {
101795
- "observedCompanyName": "Tranetechnologies",
101966
+ "observedCompanyName": "Trane Technologies",
101796
101967
  "identityEvidence": "provider_board",
101797
101968
  "contentType": "application/json",
101798
101969
  "payloadVersion": "workday-cxs:v1",
@@ -105492,7 +105663,7 @@
105492
105663
  }
105493
105664
  },
105494
105665
  "wf": {
105495
- "name": "Wf",
105666
+ "name": "Wells Fargo",
105496
105667
  "ats": "workday",
105497
105668
  "token": "wf.wd1.myworkdayjobs.com/wf/WellsFargoJobs",
105498
105669
  "sourceUrl": "https://wf.wd1.myworkdayjobs.com/en-US/WellsFargoJobs",
@@ -105501,7 +105672,7 @@
105501
105672
  "reference": "common-crawl-workday-host:wf"
105502
105673
  },
105503
105674
  "verification": {
105504
- "observedCompanyName": "Wf",
105675
+ "observedCompanyName": "Wells Fargo",
105505
105676
  "identityEvidence": "provider_board",
105506
105677
  "contentType": "application/json",
105507
105678
  "payloadVersion": "workday-cxs:v1",
@@ -105782,7 +105953,7 @@
105782
105953
  }
105783
105954
  },
105784
105955
  "wk": {
105785
- "name": "Wk",
105956
+ "name": "Wolters Kluwer",
105786
105957
  "ats": "workday",
105787
105958
  "token": "wk.wd3.myworkdayjobs.com/wk/External",
105788
105959
  "sourceUrl": "https://wk.wd3.myworkdayjobs.com/en-US/External",
@@ -105791,7 +105962,7 @@
105791
105962
  "reference": "common-crawl-workday-host:wk"
105792
105963
  },
105793
105964
  "verification": {
105794
- "observedCompanyName": "Wk",
105965
+ "observedCompanyName": "Wolters Kluwer",
105795
105966
  "identityEvidence": "provider_board",
105796
105967
  "contentType": "application/json",
105797
105968
  "payloadVersion": "workday-cxs:v1",
@@ -106960,7 +107131,7 @@
106960
107131
  }
106961
107132
  },
106962
107133
  "workday-icon": {
106963
- "name": "Icon",
107134
+ "name": "ICON plc",
106964
107135
  "ats": "workday",
106965
107136
  "token": "icon.wd3.myworkdayjobs.com/icon/broadbean_external",
106966
107137
  "sourceUrl": "https://icon.wd3.myworkdayjobs.com/en-US/broadbean_external",
@@ -106969,7 +107140,7 @@
106969
107140
  "reference": "common-crawl-workday-host:icon"
106970
107141
  },
106971
107142
  "verification": {
106972
- "observedCompanyName": "Icon",
107143
+ "observedCompanyName": "ICON plc",
106973
107144
  "identityEvidence": "provider_board",
106974
107145
  "contentType": "application/json",
106975
107146
  "payloadVersion": "workday-cxs:v1",
@@ -107649,7 +107820,7 @@
107649
107820
  }
107650
107821
  },
107651
107822
  "zeissgroup": {
107652
- "name": "Zeissgroup",
107823
+ "name": "ZEISS",
107653
107824
  "ats": "workday",
107654
107825
  "token": "zeissgroup.wd3.myworkdayjobs.com/zeissgroup/external",
107655
107826
  "sourceUrl": "https://zeissgroup.wd3.myworkdayjobs.com/en-US/external",
@@ -107658,7 +107829,7 @@
107658
107829
  "reference": "common-crawl-workday-host:zeissgroup"
107659
107830
  },
107660
107831
  "verification": {
107661
- "observedCompanyName": "Zeissgroup",
107832
+ "observedCompanyName": "ZEISS",
107662
107833
  "identityEvidence": "provider_board",
107663
107834
  "contentType": "application/json",
107664
107835
  "payloadVersion": "workday-cxs:v1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openings",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "A free, candidate-safe job-search substrate for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/abhay-avagama/hiring-agent.git" },
package/src/catalog.ts CHANGED
@@ -99,12 +99,7 @@ export function createCatalog(options: CatalogOptions): Catalog {
99
99
  const company = options.companies.find((candidate) => candidate.ats === ats && candidate.slug === slug);
100
100
  if (!company) return null;
101
101
  let job = (await fetchJobs(company)).find((candidate) => candidate.id === id) ?? null;
102
- if (job && company.ats === "workday" && !job.description.trim()) {
103
- const description = await fetchWorkdayDescription(company, job.url, fetcher);
104
- job = { ...job, description };
105
- }
106
- const spec = job && !job.description.trim() ? providerSpec(company.ats) : undefined;
107
- if (job && spec?.detail) job = { ...job, description: await spec.detail(company, job, jsonGetter(fetcher, company.name)) };
102
+ if (job && !job.description.trim()) job = { ...job, description: await fetchJobDescription(company, job, fetcher) };
108
103
  if (job && !job.description.trim()) throw new Error(`Full description unavailable for job: ${id}`);
109
104
  return job;
110
105
  },
@@ -402,6 +397,13 @@ export function workdayPostedAt(label: string | undefined, now: number = Date.no
402
397
  return new Date(now - days * 86_400_000).toISOString().slice(0, 10);
403
398
  }
404
399
 
400
+ /** The full description for a job whose listing carried none (Workday and providers with a detail endpoint); "" when there is no detail source. */
401
+ export async function fetchJobDescription(company: Company, job: Job, fetcher: Fetch): Promise<string> {
402
+ if (company.ats === "workday") return fetchWorkdayDescription(company, job.url, fetcher);
403
+ const spec = providerSpec(company.ats);
404
+ return spec?.detail ? spec.detail(company, job, jsonGetter(fetcher, company.name)) : "";
405
+ }
406
+
405
407
  async function fetchWorkdayDescription(company: Company, jobUrl: string, fetcher: Fetch): Promise<string> {
406
408
  const source = parseWorkdayToken(company.token);
407
409
  const path = new URL(jobUrl).pathname.replace(new RegExp(`^/en-US/${escapeRegExp(source.site)}`), "");
package/src/crawler.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { FetchJobsObserver } from "./catalog.ts";
2
+ import { statedExperience } from "./experience.ts";
2
3
  import { partitionFor, type Company, type CrawlFailure, type CrawlReport, type CrawlSourceResult, type Job, type JobPartition, type JobSnapshot } from "./types.ts";
3
4
 
4
5
  export interface SnapshotStore {
@@ -17,6 +18,10 @@ interface CrawlerOptions {
17
18
  sourceLimit?: number;
18
19
  workdayPageDelayMs?: number;
19
20
  workdayCountries?: string[];
21
+ /** Fetch a job's full description when its listing had none; used to read required experience. */
22
+ describe?(source: Company, job: Job): Promise<string>;
23
+ /** Only roles open to these countries, posted in the last 30 days, get a description fetch. Empty: none do. */
24
+ describeCountries?: string[];
20
25
  pacingNow?: () => number;
21
26
  pacingSleep?: (delayMs: number) => Promise<void>;
22
27
  now?: () => Date;
@@ -40,6 +45,30 @@ export function createCrawler(options: CrawlerOptions): Crawler {
40
45
  const pacingSleep = options.pacingSleep ?? ((delayMs: number) => new Promise<void>((resolve) => setTimeout(resolve, delayMs)));
41
46
  let previousStart: number | undefined;
42
47
  let pacingGate = Promise.resolve();
48
+ const describeCountries = new Set(options.describeCountries ?? []);
49
+
50
+ /** Required experience for every job: from its description, the last crawl's reading, or a paced detail fetch for recent roles in describeCountries. */
51
+ async function withExperience(source: Company, jobs: Job[], previous: Job[] | undefined): Promise<Job[]> {
52
+ const known = new Map((previous ?? []).filter((job) => job.experience !== undefined).map((job) => [job.id, job.experience]));
53
+ const since = now().getTime() - 30 * 86_400_000;
54
+ const deadline = Date.now() + DESCRIBE_BUDGET_MS;
55
+ let budget = DESCRIBE_LIMIT;
56
+ const out: Job[] = [];
57
+ for (const job of jobs) {
58
+ if (job.experience !== undefined) out.push(job);
59
+ else if (job.description.trim()) out.push({ ...job, experience: statedExperience(job.description) });
60
+ else if (known.has(job.id)) out.push({ ...job, experience: known.get(job.id) });
61
+ else if (options.describe && budget > 0 && Date.now() < deadline && job.eligibleCountries.some((code) => describeCountries.has(code)) && Date.parse(job.updatedAt ?? "") >= since) {
62
+ budget -= 1;
63
+ try {
64
+ if (options.workdayPageDelayMs) await pacingSleep(options.workdayPageDelayMs);
65
+ const description = await options.describe(source, job);
66
+ out.push(description.trim() ? { ...job, experience: statedExperience(description) } : job);
67
+ } catch { out.push(job); } // unread; the next crawl tries again
68
+ } else out.push(job);
69
+ }
70
+ return out;
71
+ }
43
72
 
44
73
  async function paceSourceStart() {
45
74
  if (!sourceStartDelayMs) return;
@@ -92,11 +121,12 @@ export function createCrawler(options: CrawlerOptions): Crawler {
92
121
  const controller = new AbortController();
93
122
  const timer = setTimeout(() => controller.abort(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
94
123
  try {
95
- const jobs = await options.fetchJobs(source, controller.signal, {
124
+ const listed = await options.fetchJobs(source, controller.signal, {
96
125
  onBackoff: ({ status, delayMs }) => { metric.backoffMs += delayMs; if (status === 429) metric.throttles += 1; },
97
126
  workdayPageDelayMs: options.workdayPageDelayMs,
98
127
  workdayCountries: options.workdayCountries,
99
128
  });
129
+ const jobs = await withExperience(source, listed, partitionFor(partitions, source.slug)?.jobs);
100
130
  partitions[source.slug] = { fetchedAt: now().toISOString(), jobs };
101
131
  succeeded += 1;
102
132
  metric.status = "succeeded";
@@ -136,6 +166,10 @@ export function createCrawler(options: CrawlerOptions): Crawler {
136
166
  };
137
167
  }
138
168
 
169
+ // ponytail: per-source caps keep one crawl bounded; a big backlog (first run) drains over a few nights.
170
+ const DESCRIBE_LIMIT = 150;
171
+ const DESCRIBE_BUDGET_MS = 180_000;
172
+
139
173
  function partitionTime(value: string | undefined): number {
140
174
  const timestamp = Date.parse(value ?? "");
141
175
  return Number.isFinite(timestamp) ? timestamp : Number.NEGATIVE_INFINITY;
@@ -46,7 +46,10 @@ export async function resolveEmployers(signalsPath: string, options: { catalogPa
46
46
  report.requests += 1;
47
47
  try { const reply = await fetcher(`https://api.smartrecruiters.com/v1/companies/${encodeURIComponent(guess)}/postings?limit=1`, { signal: AbortSignal.timeout(timeout) }); if (!reply.ok) { await reply.body?.cancel().catch(() => undefined); return null; } const body = await reply.json() as { totalFound?: number }; return (body.totalFound ?? 0) > 0 ? `https://jobs.smartrecruiters.com/${guess}` : null; } catch { return null; }
48
48
  },
49
- async (guess) => { const reply = await ok(`https://apply.workable.com/api/v1/widget/accounts/${encodeURIComponent(guess)}`); return reply?.ok ? `https://apply.workable.com/${guess}/` : null; },
49
+ async (guess) => { // Workable answers any account name; only a board with jobs proves the company exists
50
+ report.requests += 1;
51
+ try { const reply = await fetcher(`https://apply.workable.com/api/v1/widget/accounts/${encodeURIComponent(guess)}`, { signal: AbortSignal.timeout(timeout) }); if (!reply.ok) { await reply.body?.cancel().catch(() => undefined); return null; } const body = await reply.json() as { jobs?: unknown[] }; return Array.isArray(body.jobs) && body.jobs.length > 0 ? `https://apply.workable.com/${guess}/` : null; } catch { return null; }
52
+ },
50
53
  async (guess) => { if (!/^[a-z0-9-]+$/.test(guess)) return null; const reply = await ok(`https://${guess}.keka.com/careers`); if (!reply?.ok) return null; try { const shell = await (await fetcher(`https://${guess}.keka.com/careers`, { signal: AbortSignal.timeout(timeout) })).text(); const org = /\/ats\/documents\/([0-9a-f-]{36})\//i.exec(shell)?.[1]; return org ? `https://${guess}.keka.com/careers/api/embedjobs/default/active/${org.toLowerCase()}` : null; } catch { return null; } },
51
54
  ];
52
55
  const leads: EnrichmentLead[] = [];
@@ -0,0 +1,45 @@
1
+ /** Years of experience a posting asks for, read from its own text. */
2
+ export interface Experience { min: number; max?: number }
3
+
4
+ const WORDS: Record<string, number> = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, ten: 10, twelve: 12, fifteen: 15 };
5
+ const NUM = String.raw`(\d{1,2}(?:\.\d)?|${Object.keys(WORDS).join("|")})`;
6
+ const RANGE = String.raw`(?<![\d.])${NUM}\s*\+?\s*(?:(?:-|–|—|to)\s*${NUM}\s*)?\+?\s*(?:years?|yrs?)(?:'|’)?`;
7
+ // "5+ years of hands-on Java experience" or "Experience: 2-5 years" / "Years of experience 6 to 8 years".
8
+ const YEARS_THEN_EXPERIENCE = new RegExp(String.raw`${RANGE}(?:\s+of)?(?:\s+[\w/&,'’()-]+){0,6}?\s+(?:experience|exp)\b`, "gi");
9
+ const EXPERIENCE_THEN_YEARS = new RegExp(String.raw`\b(?:experience|exp)\b[^.\n\d]{0,30}?${RANGE}`, "gi");
10
+ // Company history reads the same way ("our 135 years of experience"); skip matches that talk about the employer.
11
+ const ABOUT_EMPLOYER = /\b(?:we|our|company|firm|founded|established|history|legacy|heritage)\b[^.\n?!:;•·-]*$/i;
12
+ const MIN_PREFIX = /\b(?:minimum|min\.?|at least|atleast)\s*(?:of\s*)?$/i;
13
+
14
+ function value(text: string | undefined): number | undefined {
15
+ if (!text) return undefined;
16
+ return WORDS[text.toLowerCase()] ?? Number(text);
17
+ }
18
+
19
+ /** The first experience requirement the text states, or null when it states none. */
20
+ export function statedExperience(text: string): Experience | null {
21
+ const found: Array<{ index: number; experience: Experience }> = [];
22
+ for (const pattern of [YEARS_THEN_EXPERIENCE, EXPERIENCE_THEN_YEARS]) {
23
+ for (const match of text.matchAll(pattern)) {
24
+ const [low, high] = [value(match[1]), value(match[2])];
25
+ if (low === undefined || !Number.isFinite(low)) continue;
26
+ // Requirements rarely pass 20 years; bigger numbers are nearly always an employer's history.
27
+ if (low > 20 || (high !== undefined && (high < low || high > 40))) continue;
28
+ const before = text.slice(Math.max(0, match.index - 60), match.index);
29
+ if (ABOUT_EMPLOYER.test(before) && !MIN_PREFIX.test(before)) continue;
30
+ found.push({ index: match.index, experience: high !== undefined && high !== low ? { min: low, max: high } : { min: low } });
31
+ }
32
+ }
33
+ found.sort((left, right) => left.index - right.index);
34
+ return found[0]?.experience ?? null;
35
+ }
36
+
37
+ /** Entry-level titles (intern, trainee, fresher, graduate) as a rough 0–1 year estimate when the posting states nothing. */
38
+ export function titleExperience(title: string): Experience | undefined {
39
+ return /\b(?:intern|internship|trainee|freshers?|graduate|apprentice(?:ship)?|entry[- ]level)\b/i.test(title) ? { min: 0, max: 1 } : undefined;
40
+ }
41
+
42
+ /** "2–5 yrs", "5+ yrs". */
43
+ export function experienceLabel(experience: Experience): string {
44
+ return experience.max === undefined ? `${experience.min}+ yrs` : `${experience.min}–${experience.max} yrs`;
45
+ }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export const companies: Company[] = Object.entries(companyData).map(([slug, valu
17
17
 
18
18
  export const catalog = createCatalog({ companies });
19
19
  export { createCatalog } from "./catalog.ts";
20
+ export { experienceLabel, statedExperience, titleExperience, type Experience } from "./experience.ts";
20
21
  export type { Catalog } from "./catalog.ts";
21
22
  export type { Ats, Company, CrawlReport, Job, JobPartition, JobSnapshot, JobSummary, SearchQuery } from "./types.ts";
22
23
  export { createCrawlReporter, fetchSeedSnapshot, resolveAggregatorUrl } from "./crawl-reporting.ts";
@@ -1,3 +1,4 @@
1
+ import { experienceLabel } from "./experience.ts";
1
2
  import { validateCandidateProfileEvidence, type CandidateProfile } from "./candidate-profile.ts";
2
3
  import { isEligibleForCountry, normalizeLocation } from "./locations.ts";
3
4
  import { detectRequirementTerms, findTransferability, matchesExactSkillEvidence, requiresExactSkillEvidence, type TransferabilityKind } from "./requirement-vocabulary.ts";
@@ -258,6 +259,13 @@ function supportedRequirements(profile: CandidateProfile, requirements: string[]
258
259
  const seniorityTerms = ["manager", "principal", "staff", "lead", "senior", "mid", "junior", "intern"] as const;
259
260
  function seniorityAlignment(profile: CandidateProfile, intent: CandidateIntent, job: Job): { score: number; reason?: string } {
260
261
  const requested = intent.seniority?.map((value) => value.toLocaleLowerCase()) ?? profile.inferences.filter((inference) => inference.kind === "seniority").map((inference) => inference.value);
262
+ const resumeYears = profile.inferences.find((inference) => inference.kind === "approximate_experience_years")?.value;
263
+ if (job.experience && typeof resumeYears === "number" && !intent.seniority?.length) {
264
+ // The posting's own range is better evidence than a seniority word in the title. A year short or a few over still fits.
265
+ const asked = experienceLabel(job.experience);
266
+ const fits = resumeYears >= job.experience.min - 1 && (job.experience.max === undefined || resumeYears <= job.experience.max + 3);
267
+ return { score: fits ? 3 : -3, reason: `posting asks for ${asked}; resume shows about ${resumeYears} years` };
268
+ }
261
269
  const jobSeniority = seniorityTerms.find((term) => includesPhrase(job.title, term));
262
270
  if (!jobSeniority) return { score: 0 };
263
271
  if (!requested.length) {
package/src/local-jobs.ts CHANGED
@@ -15,6 +15,8 @@ interface LocalJobsOptions {
15
15
  sourceLimit?: number;
16
16
  workdayPageDelayMs?: number;
17
17
  workdayCountries?: string[];
18
+ describe?(source: Company, job: Job): Promise<string>;
19
+ describeCountries?: string[];
18
20
  now?: () => Date;
19
21
  onCrawled?(source: Company, partition: JobPartition): void | Promise<void>;
20
22
  }
package/src/mcp.ts CHANGED
@@ -21,6 +21,7 @@ interface ToolHandler {
21
21
  export const FLOW_INSTRUCTIONS = [
22
22
  "Openings flow: 1) call prepare_job_search for the person's countries; it is normally ready in one call. 2) Ask for the resume before exploring roles, because recommend_jobs ranks by evidence from it. 3) If the person declines a resume, use search_jobs with their keywords. 4) If they decline that too, use your judgement.",
23
23
  "Freshness: results start with roles posted in the last 7 days and widen to 14, 30, then everything only when fewer than 5 appear; each result carries age (new, older, stale, undated) and postedDaysAgo. Say which window results came from and present older or stale roles as possibly still open, never as current.",
24
+ "Experience: a job's experience field is the years its posting states ({ min, max }); null means the posting states none, and absent means it was not read. Quote it as the employer's requirement.",
24
25
  "Every match carries applyUrl, the employer's own posting; include it. When recommend_jobs reports no_matches, relay its explanation and nextMoves instead of searching silently.",
25
26
  ].join("\n");
26
27
 
package/src/runtime.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { join } from "node:path";
2
2
  import type { SnapshotStore } from "./crawler.ts";
3
3
  import type { Company, SearchQuery } from "./types.ts";
4
- import { fetchSourceJobs } from "./catalog.ts";
4
+ import { fetchJobDescription, fetchSourceJobs } from "./catalog.ts";
5
5
  import { createCrawlReporter, fetchSeedSnapshot, resolveAggregatorUrl } from "./crawl-reporting.ts";
6
6
  import { createUsageReporter, type UsageReporter } from "./usage.ts";
7
7
  import { VERSION } from "./version.ts";
@@ -33,6 +33,9 @@ export function createRuntime(options: { dataDir?: string; concurrency?: number;
33
33
  sourceLimit: options.sourceLimit,
34
34
  workdayPageDelayMs: options.workdayPageDelayMs,
35
35
  workdayCountries,
36
+ // Server crawls set OPENINGS_DESCRIBE_COUNTRIES (IN) to read required experience from Workday detail pages; installs skip the extra requests.
37
+ describe: (source, job) => fetchJobDescription(source, job, globalThis.fetch),
38
+ describeCountries: (process.env.OPENINGS_DESCRIBE_COUNTRIES ?? "").split(",").map((code) => code.trim().toUpperCase()).filter((code) => /^[A-Z]{2}$/.test(code)),
36
39
  onCrawled,
37
40
  });
38
41
  const recommender = createJobRecommender({ sources: companies, store, crawl: local.crawl });
package/src/types.ts CHANGED
@@ -66,6 +66,8 @@ export interface SourceVerificationResult {
66
66
  rejected: RejectedSource[];
67
67
  }
68
68
 
69
+ import type { Experience } from "./experience.ts";
70
+
69
71
  export type WorkMode = "remote" | "hybrid" | "onsite" | "unknown";
70
72
  export type EligibilityConfidence = "explicit" | "inferred" | "unknown";
71
73
 
@@ -83,6 +85,8 @@ export interface JobSummary {
83
85
  url: string;
84
86
  /** Posting date when the board exposes one (Workday's relative label is approximate), otherwise the board's last-update time. */
85
87
  updatedAt?: string;
88
+ /** Years of experience the posting states, read from its description. null: the description states none. Absent: not read yet. */
89
+ experience?: Experience | null;
86
90
  /** Days since the posting date at the time of the search; absent when the board gave no date. */
87
91
  postedDaysAgo?: number;
88
92
  /** new = 7 days or less, older = 8 to 30, stale = beyond 30, undated = no posting date. Present it as such; a stale listing may still be open. */
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.30";
1
+ export const VERSION = "0.1.32";