vortexa-claude-skills 1.0.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.
@@ -0,0 +1,651 @@
1
+ # Reference Endpoints
2
+
3
+ > Complete reference for Products, Geographies, Vessels, and Corporations entity endpoints.
4
+ > For the entity resolution workflow (how to resolve names to IDs), see entity-resolution.md (pre-loaded).
5
+ > This doc covers what the API supports -- full parameter tables, hierarchies, and SDK/REST examples.
6
+
7
+ ---
8
+
9
+ ## Entity Hierarchy Overview
10
+
11
+ All Vortexa data endpoints require 64-char hex IDs. Use these reference endpoints to resolve human-readable names.
12
+
13
+ | Entity Type | SDK Class | Hierarchy (widest to most specific) | Key Filter |
14
+ |---|---|---|---|
15
+ | Product | `Products` | group > group_product > category > grade | `filter_layer` |
16
+ | Geography | `Geographies` | wider_shipping_region > shipping_region_v2 > region > country > port > terminal | `filter_layer` |
17
+ | Corporation | `Corporations` | effective_controller > commercial_owner > time_charterer > charterer | (none in SDK) |
18
+ | Vessel | `Vessels` | oil / lpg / lng > grouped class > detailed class | `vessel_classes` |
19
+
20
+ ---
21
+
22
+ ## Products
23
+
24
+ ### Hierarchy
25
+
26
+ ```
27
+ group (widest)
28
+ +-- group_product
29
+ +-- category
30
+ +-- grade (most specific)
31
+ ```
32
+
33
+ | Layer | Example | When to Use |
34
+ |---|---|---|
35
+ | `group` | Crude & Condensates, Clean Petroleum Products, Dirty Petroleum Products, LNG, LPG/NGL | High-level commodity analysis |
36
+ | `group_product` | Crude, Condensates, Gasoline/Naphtha, Fuel Oil, Gasoil/Diesel | Product family breakdown |
37
+ | `category` | Medium-Sour, Light-Sweet, Diesel/Gasoil | Mid-level product classification |
38
+ | `grade` | Arab Light, Bonny Light, Grane | Specific crude grade or refined product |
39
+
40
+ ### Product Groups (Top Level)
41
+
42
+ These are the root-level product groups in Vortexa:
43
+ - Crude & Condensates
44
+ - Clean Petroleum Products
45
+ - Dirty Petroleum Products
46
+ - LNG
47
+ - LPG/NGL
48
+ - Chemicals
49
+ - Vegetable Oils
50
+
51
+ ### SDK Methods
52
+
53
+ ```python
54
+ from vortexasdk import Products
55
+
56
+ # Search by name with layer filter
57
+ df = Products().search(term="Arab Light", filter_layer="grade").to_df()
58
+
59
+ # Search multiple terms
60
+ df = Products().search(term=["diesel", "fuel oil", "grane"]).to_df("all")
61
+
62
+ # Get children of a parent product
63
+ df = Products().search(product_parent="<parent_id>").to_df()
64
+
65
+ # Lookup by ID
66
+ record = Products().reference(id="abc123...")
67
+ ```
68
+
69
+ **search() parameters:**
70
+
71
+ | Parameter | Type | Description |
72
+ |---|---|---|
73
+ | `term` | str or List[str] | Product name(s) to search |
74
+ | `ids` | str or List[str] | Specific product IDs |
75
+ | `product_parent` | str or List[str] | Parent product ID(s) -- returns direct children |
76
+ | `exact_term_match` | bool | Default `False` |
77
+ | `filter_layer` | str | One of: `group`, `group_product`, `category`, `grade` |
78
+
79
+ **Default DataFrame columns:** `id`, `name`, `layer.0`, `parent.0.name`
80
+
81
+ **Full columns** (use `columns="all"`): includes `parent.0.id`, `parent.0.layer.0`, `meta.api_min`, `meta.api_max`, `meta.sulphur_min`, `meta.sulphur_max`, `ref_type`, `leaf`
82
+
83
+ ### REST API Alternative
84
+
85
+ ```python
86
+ import requests
87
+
88
+ base = "https://api.vortexa.com"
89
+ headers = {"x-api-key": VORTEXA_API_KEY}
90
+
91
+ payload = {"term": "Arab Light", "filter_layer": ["grade"], "size": 10}
92
+ resp = requests.post(f"{base}/v5/reference/products", headers=headers, json=payload)
93
+ products = resp.json()["data"]
94
+ ```
95
+
96
+ **REST-specific parameters (not in SDK):**
97
+
98
+ | Parameter | Type | Description |
99
+ |---|---|---|
100
+ | `product_parent` | str | Parent ID or `"TOP_LEVEL"` to get root-level products |
101
+ | `product_ancestor` | str[] | Ancestor IDs (returns all descendants) |
102
+ | `allowTopLevelProducts` | bool | Default `true`. Include top-level products |
103
+
104
+ ### Product Entries in API Responses
105
+
106
+ Cargo movement/voyage responses embed one product entry per hierarchy layer:
107
+
108
+ ```json
109
+ [
110
+ {"id": "...", "layer": "group", "label": "Crude & Condensates", "probability": 0.94, "source": "model"},
111
+ {"id": "...", "layer": "group_product", "label": "Crude", "probability": 0.94, "source": "model"},
112
+ {"id": "...", "layer": "category", "label": "Medium-Sour", "probability": 0.94, "source": "model"},
113
+ {"id": "...", "layer": "grade", "label": "Arab Light", "probability": 0.94, "source": "model"}
114
+ ]
115
+ ```
116
+
117
+ To extract a specific level: filter where `layer == "grade"` (or whichever level you need).
118
+
119
+ ### CRITICAL RULES -- Products
120
+
121
+ - **ALWAYS** specify `filter_layer` to avoid matching the wrong hierarchy level
122
+ - **ALWAYS** verify the `layer` and `name` columns in results -- fuzzy matching may return grades when you want the group
123
+ - **NEVER** assume "crude" matches only one product -- without `filter_layer`, it returns group, group_product, and grade matches
124
+ - **ALWAYS** set `size` >= 10 for resolution searches (default `size=1` may return the wrong match)
125
+
126
+ ---
127
+
128
+ ## Geographies
129
+
130
+ ### Hierarchy
131
+
132
+ ```
133
+ wider_shipping_region (widest)
134
+ +-- shipping_region_v2
135
+ +-- region
136
+ +-- country
137
+ +-- port
138
+ +-- terminal (most specific)
139
+ ```
140
+
141
+ **Additional geography layers (not in main hierarchy):**
142
+ - `sts_zone` -- Ship-to-ship transfer zones
143
+ - `waypoint` -- Chokepoints and transit points (e.g. Suez, Panama)
144
+ - `trading_block`, `trading_region`, `trading_subregion` -- Trade-oriented groupings
145
+ - `country_zone`, `state_or_province` -- Sub-country divisions
146
+ - `basin` -- Ocean basins
147
+ - `storage`, `storage_terminal` -- Onshore storage locations
148
+
149
+ ### SDK Methods
150
+
151
+ ```python
152
+ from vortexasdk import Geographies
153
+
154
+ # Search by name with layer filter
155
+ df = Geographies().search(term="Rotterdam", filter_layer="port").to_df()
156
+
157
+ # Search multiple terms
158
+ df = Geographies().search(term=["Liverpool", "Southampton"]).to_df()
159
+
160
+ # Exact match only
161
+ df = Geographies().search(term="China", exact_term_match=True, filter_layer="country").to_df()
162
+
163
+ # Lookup by ID
164
+ record = Geographies().reference(id="abc123...")
165
+ ```
166
+
167
+ **search() parameters:**
168
+
169
+ | Parameter | Type | Description |
170
+ |---|---|---|
171
+ | `term` | str or List[str] | Geography name(s) to search |
172
+ | `exact_term_match` | bool | Default `False`. If `True`, only exact name matches |
173
+ | `filter_layer` | str | One of: `terminal`, `port`, `country`, `country_zone`, `shipping_region`, `shipping_region_v2`, `wider_shipping_region`, `region`, `alternative_region`, `trading_block`, `trading_region`, `trading_subregion`, `state_or_province`, `sts_zone`, `waypoint`, `storage`, `storage_terminal`, `basin`, `root` |
174
+
175
+ **Default DataFrame columns:** `id`, `name`, `layer`
176
+
177
+ **REST-specific parameters (not in SDK):**
178
+
179
+ | Parameter | Type | Description |
180
+ |---|---|---|
181
+ | `geography_parent` | str[] | Parent ID(s) or `"TOP_LEVEL"` to get root-level geographies |
182
+ | `only_root` | bool | Omit parent/hierarchy data from results |
183
+
184
+ ### Common Geography Lookups
185
+
186
+ | Common Name / Abbreviation | filter_layer | Vortexa Name | Notes |
187
+ |---|---|---|---|
188
+ | "China", "US", "Saudi Arabia" | `country` | China, United States, Saudi Arabia | Country names |
189
+ | "Rotterdam", "Fujairah", "Ras Tanura" | `port` | Rotterdam [NL], Fujairah, Ras Tanura | Port names |
190
+ | "Middle East", "Asia", "Europe" | `region` | Middle East/North Africa, Asia, Europe | Broad regions |
191
+ | "AG", "Arabian Gulf", "Persian Gulf" | `shipping_region_v2` | Arabian/Persian Gulf | Shipping regions |
192
+ | "USG", "US Gulf Coast" | `shipping_region_v2` | US Gulf Coast | Shipping regions |
193
+ | "WAF", "West Africa" | `shipping_region_v2` | West Africa | Shipping regions |
194
+ | "ARA" | `port` (multiple) | Amsterdam, Rotterdam, Antwerp | Resolve each port separately |
195
+ | "PADD I", "East Coast" | `shipping_region_v2` | US Atlantic Coast | US regional |
196
+ | "Suez Canal", "Panama Canal" | `waypoint` | Suez Canal, Panama Canal | Chokepoints |
197
+
198
+ ### REST API Alternative
199
+
200
+ ```python
201
+ import requests
202
+
203
+ base = "https://api.vortexa.com"
204
+ headers = {"x-api-key": VORTEXA_API_KEY}
205
+
206
+ payload = {"term": "Rotterdam", "filter_layer": ["port"], "size": 10}
207
+ resp = requests.post(f"{base}/v5/reference/geographies", headers=headers, json=payload)
208
+ geos = resp.json()["data"]
209
+ ```
210
+
211
+ ### Geography Entries in API Responses
212
+
213
+ Location arrays in cargo events contain one entry per hierarchy layer:
214
+
215
+ ```json
216
+ [
217
+ {"id": "...", "layer": "terminal", "label": "Poleng Marine Terminal", "probability": 1, "source": "model"},
218
+ {"id": "...", "layer": "port", "label": "Poleng Field [ID]", "probability": 1, "source": "model"},
219
+ {"id": "...", "layer": "country", "label": "Indonesia", "probability": 1, "source": "model"},
220
+ {"id": "...", "layer": "region", "label": "Asia", "probability": 1, "source": "model"}
221
+ ]
222
+ ```
223
+
224
+ ### CRITICAL RULES -- Geographies
225
+
226
+ - **ALWAYS** specify `filter_layer` -- "China" without a layer returns ports, regions, AND the country
227
+ - **NEVER** use a port ID when a country was intended (volumes will be drastically wrong)
228
+ - **NEVER** search geographies without `filter_layer` in production queries
229
+ - **ALWAYS** use `exact_term_match=True` for well-known names to avoid fuzzy noise
230
+ - **ALWAYS** set `size` >= 10 for resolution searches
231
+
232
+ ---
233
+
234
+ ## Vessels
235
+
236
+ ### SDK Methods
237
+
238
+ ```python
239
+ from vortexasdk import Vessels
240
+
241
+ # Search by name
242
+ df = Vessels().search(term="ocean", vessel_classes="oil_vlcc").to_df()
243
+
244
+ # Search by IMO
245
+ df = Vessels().search(ids=[9779836]).to_df(columns="all")
246
+
247
+ # Filter by class and scrubber
248
+ df = Vessels().search(
249
+ vessel_classes=["oil_aframax", "oil_suezmax"],
250
+ vessel_scrubbers="inc"
251
+ ).to_df()
252
+
253
+ # Lookup by ID
254
+ record = Vessels().reference(id="abc123...")
255
+ ```
256
+
257
+ **search() parameters:**
258
+
259
+ | Parameter | Type | Description |
260
+ |---|---|---|
261
+ | `term` | str or List[str] | Vessel name(s) to search. Also matches `related_names`. |
262
+ | `ids` | str or List[str] | Vessel IDs, IMO numbers, or MMSI numbers |
263
+ | `vessel_classes` | str or List[str] | Vessel class filter (see class reference below) |
264
+ | `vessel_scrubbers` | str | `"disabled"` (default), `"inc"`, or `"exc"` |
265
+ | `exact_term_match` | bool | Default `False` |
266
+
267
+ **Default DataFrame columns:** `id`, `name`, `imo`, `vessel_class`
268
+
269
+ **Additional columns** (use `columns="all"`): `mmsi`, `dwt`, `cubic_capacity`, `related_names`, `year`, `flag`, `scrubber`, `ice_class`, `propulsion`, `tags`
270
+
271
+ **REST-specific parameters (not in SDK):**
272
+
273
+ | Parameter | Type | Description |
274
+ |---|---|---|
275
+ | `vessel_tags` | object[] | Filter by vessel tags (e.g. dark activity, FSO) |
276
+ | `vessel_tags_excluded` | object[] | Exclude vessels with these tags |
277
+ | `dwt_min` / `dwt_max` | number | DWT range filter (0-550,000) |
278
+ | `ship_to_ship_only` | bool | Only STS-capable vessels |
279
+
280
+ ### Vessel Class Reference
281
+
282
+ **User Term to API Vessel Class mapping:**
283
+
284
+ | User Says | API `vessel_classes` Value | Typical Size |
285
+ |---|---|---|
286
+ | "VLCC" | `oil_vlcc` | 200,000+ DWT |
287
+ | "Suezmax" | `oil_suezmax` | 120,000-200,000 DWT |
288
+ | "Aframax" | `oil_aframax` | 80,000-120,000 DWT |
289
+ | "Panamax" (oil) | `oil_panamax` | 55,000-80,000 DWT |
290
+ | "MR2", "Handymax" | `oil_handymax` or `oil_mr2` | 40,000-55,000 DWT |
291
+ | "MR1", "Handysize" | `oil_handysize` or `oil_mr1` | 25,000-40,000 DWT |
292
+ | "LR1" | `oil_lr1` | 55,000-80,000 DWT |
293
+ | "LR2" | `oil_lr2` | 80,000-120,000 DWT |
294
+ | "LR3" | `oil_lr3` | 120,000-200,000 DWT |
295
+ | "Coastal tanker" | `oil_coastal` | <25,000 DWT |
296
+ | "VLGC" | `lpg_vlgc` | 60,000+ CBM |
297
+ | "LGC" | `lpg_lgc` | 35,000-60,000 CBM |
298
+ | "MGC" | `lpg_mgc` | 15,000-35,000 CBM |
299
+ | "Q-Flex", "Q-Max" | `lng_q_flex`, `lng_q_max` | Qatar Q-class mega |
300
+ | "LNG carrier" | `lng_conventional_lng` | Standard LNG |
301
+ | "All oil tankers" | `oil` | All oil subclasses |
302
+ | "All LPG carriers" | `lpg` | All LPG subclasses |
303
+ | "All LNG carriers" | `lng` | All LNG subclasses |
304
+
305
+ **Grouped classes** (each group aggregates multiple detailed classes):
306
+
307
+ | Grouped Class | Detailed Classes |
308
+ |---|---|
309
+ | `oil_suezmax_lr3` | `oil_suezmax`, `oil_lr3` |
310
+ | `oil_aframax_lr2` | `oil_aframax`, `oil_lr2` |
311
+ | `oil_panamax_lr1` | `oil_panamax`, `oil_lr1` |
312
+ | `oil_handymax_mr2` | `oil_handymax`, `oil_mr2` |
313
+ | `oil_handysize_mr1` | `oil_handysize`, `oil_mr1` |
314
+ | `lpg_vlgc_vlec` | `lpg_vlgc`, `lpg_vlec` |
315
+ | `lpg_sgc` | `lpg_handysize`, `lpg_coasters` |
316
+ | `lng_conventional_lng` | `lng_two_stroke`, `lng_tfde_dfde`, `lng_steam`, `lng_ssd` |
317
+
318
+ Top-level aggregates: `oil`, `lpg`, `lng` -- match all subclasses within that group.
319
+
320
+ ### Vessel Entities in API Responses
321
+
322
+ Vessel objects in cargo movement responses contain:
323
+
324
+ | Field | Type | Description |
325
+ |---|---|---|
326
+ | `id` | string | Vortexa vessel ID (64-char hex) |
327
+ | `imo` | number | IMO number |
328
+ | `mmsi` | number | MMSI number |
329
+ | `name` | string | Vessel name |
330
+ | `dwt` | number | Deadweight tonnage |
331
+ | `cubic_capacity` | number | Cubic capacity (m3) |
332
+ | `vessel_class` | string | Vessel class code |
333
+ | `corporate_entities` | array | Array of corporate entity objects |
334
+ | `start_timestamp` | string | When cargo was loaded onto this vessel |
335
+ | `end_timestamp` | string or null | When cargo left (null if still onboard) |
336
+ | `voyage_id` | string or null | Linked voyage ID |
337
+ | `tags` | array | Time-bound vessel tags (e.g. FSO, dark activity) |
338
+ | `status` | string | e.g. `vessel_status_laden_known`, `vessel_status_ballast` |
339
+ | `year` | number or null | Build year |
340
+ | `flag` | array | Flag state info |
341
+ | `scrubber` | array | Scrubber info |
342
+
343
+ ### REST API Alternative
344
+
345
+ ```python
346
+ import requests
347
+
348
+ base = "https://api.vortexa.com"
349
+ headers = {"x-api-key": VORTEXA_API_KEY}
350
+
351
+ # Search by IMO
352
+ payload = {"term": [9779836], "size": 10}
353
+ resp = requests.post(f"{base}/v5/reference/vessels", headers=headers, json=payload)
354
+ vessels = resp.json()["data"]
355
+
356
+ # Get vessel by ID
357
+ vessel_id = "1f6d6ab80d90846cedddf1a4b470faf566fd72595e853bc769185fac6ae7af70"
358
+ resp = requests.get(f"{base}/v5/reference/vessels/{vessel_id}", headers=headers)
359
+ vessel = resp.json()["data"][0]
360
+
361
+ # Get IDs only (lighter response)
362
+ payload = {"vessel_classes": ["oil_vlcc"], "size": 500}
363
+ resp = requests.post(f"{base}/v5/reference/vessels-ids", headers=headers, json=payload)
364
+ # Supports return_type: "short" for 16-char truncated IDs
365
+ ```
366
+
367
+ ### CRITICAL RULES -- Vessels
368
+
369
+ - **NEVER** pass IMO numbers as strings -- the API requires integers in the `ids` or `term` array
370
+ - **ALWAYS** use `vessel_classes` filter in Vessels endpoint for name/IMO/class lookup only
371
+ - Vessel class filtering for data queries (CargoMovements/Voyages) happens via `filter_vessel_classes` in the query, not in the Vessels reference endpoint
372
+ - For vessel class filtering in CM/Voyages: use `filter_vessel_classes=["oil_vlcc"]` directly -- no ID resolution needed
373
+ - **ALWAYS** set `size` >= 10 for resolution searches
374
+
375
+ ---
376
+
377
+ ## Corporations
378
+
379
+ ### SDK Methods
380
+
381
+ ```python
382
+ from vortexasdk import Corporations
383
+
384
+ # Search by name
385
+ df = Corporations().search(term="BAHRI").to_df()
386
+
387
+ # Exact match
388
+ df = Corporations().search(term="CHEVRON", exact_term_match=True).to_df()
389
+
390
+ # Lookup by ID
391
+ record = Corporations().reference(id="abc123...")
392
+ ```
393
+
394
+ **search() parameters:**
395
+
396
+ | Parameter | Type | Description |
397
+ |---|---|---|
398
+ | `term` | str or List[str] | Corporation name(s) to search |
399
+ | `exact_term_match` | bool | Default `False` |
400
+
401
+ **Default DataFrame columns:** `id`, `name`, `corporate_entity_type`
402
+
403
+ ### Corporate Hierarchy
404
+
405
+ ```
406
+ effective_controller (ultimate owner)
407
+ +-- commercial_owner
408
+ +-- time_charterer
409
+ +-- charterer (voyage-level)
410
+ ```
411
+
412
+ Additional corporate entity types (API only):
413
+ - `cargo_trader` -- Commodity trading house
414
+ - `shipper` -- Party shipping the cargo
415
+ - `consignee` -- Receiving party
416
+ - `buyer_seller` -- Transaction counterparty
417
+
418
+ ### REST API Alternative (Required for Entity Type Filtering)
419
+
420
+ The SDK `Corporations` class wraps the API's `/reference/charterers` endpoint but does **NOT** expose `corporateEntityType` filtering. Use REST directly when you need to filter by role:
421
+
422
+ ```python
423
+ import requests
424
+
425
+ base = "https://api.vortexa.com"
426
+ headers = {"x-api-key": VORTEXA_API_KEY}
427
+
428
+ # Search corporations by entity type (REST only)
429
+ payload = {
430
+ "term": "CHEVRON",
431
+ "corporateEntityType": ["effective_controller"],
432
+ "size": 10
433
+ }
434
+ resp = requests.post(f"{base}/v5/reference/charterers", headers=headers, json=payload)
435
+ corps = resp.json()["data"]
436
+ ```
437
+
438
+ **REST `corporateEntityType` values:** `effective_controller`, `charterer`, `time_charterer`, `commercial_owner`, `cargo_trader`, `shipper`, `consignee`, `buyer_seller`, `all`
439
+
440
+ ### Corporate Entities in API Responses
441
+
442
+ The `corporate_entities` array in vessel objects contains one entry per role:
443
+
444
+ ```json
445
+ [
446
+ {"id": "...", "layer": "effective_controller", "label": "BAHRI", "probability": 1, "source": "external"},
447
+ {"id": "...", "layer": "charterer", "label": "CHEVRON", "probability": 1, "source": "external"}
448
+ ]
449
+ ```
450
+
451
+ Not all roles are always present -- a vessel may have an effective_controller but no charterer.
452
+
453
+ ### CRITICAL RULES -- Corporations
454
+
455
+ - **SDK limitation:** `Corporations` class does NOT expose `corporateEntityType` filter -- use REST API for role-based filtering
456
+ - **ALWAYS** use `exact_term_match=True` when searching well-known company names to avoid fuzzy noise (e.g. "COS" returns COSCO, COSMO OIL, etc.)
457
+ - **NEVER** assume a single corporate entity has only one role -- the same company can appear as both effective_controller and charterer
458
+ - **ALWAYS** set `size` >= 10 for resolution searches
459
+
460
+ ---
461
+
462
+ ## Additional Reference Endpoints
463
+
464
+ These endpoints are less frequently used but available for specialized queries.
465
+
466
+ ### Attributes
467
+
468
+ Resolve vessel attribute IDs (propulsion, scrubber type, ice class) to human-readable labels.
469
+
470
+ ```python
471
+ from vortexasdk import Attributes
472
+
473
+ # Get all scrubber types
474
+ df = Attributes().search(type="scrubber").to_df()
475
+
476
+ # Resolve specific attribute IDs from vessel data
477
+ propulsion_id = "3ace0e050724707b"
478
+ attr = Attributes().search(ids=[propulsion_id]).to_df()
479
+ # Returns: id, name="DFDE", type="propulsion"
480
+ ```
481
+
482
+ **search() parameters:**
483
+
484
+ | Parameter | Type | Description |
485
+ |---|---|---|
486
+ | `type` | str | One of: `ice_class`, `propulsion`, `scrubber` |
487
+ | `term` | str or List[str] | Attribute name to search |
488
+ | `ids` | str or List[str] | Specific attribute IDs to resolve |
489
+
490
+ ### Asset Tanks
491
+
492
+ Search onshore oil storage tanks.
493
+
494
+ ```python
495
+ from vortexasdk import AssetTanks
496
+
497
+ df = AssetTanks().search(storage_type=["refinery"]).to_df()
498
+ df = AssetTanks().search(location_ids=["<geo_id>"], crude_confidence=["confirmed"]).to_df()
499
+ ```
500
+
501
+ **Key parameters:** `storage_type` (refinery/commercial/spr), `crude_confidence` (confirmed/probable/unlikely), `location_ids`, `corporate_entity_ids`
502
+
503
+ ### Storage Terminals
504
+
505
+ ```python
506
+ from vortexasdk import StorageTerminals
507
+
508
+ df = StorageTerminals().search(term=["Military"]).to_df()
509
+ ```
510
+
511
+ ### Refineries
512
+
513
+ ```python
514
+ from vortexasdk import Refineries
515
+
516
+ df = Refineries().search(term="San").to_df(columns=["name", "country_name"])
517
+ df = Refineries().search(status="Active").to_df(columns="all")
518
+ df = Refineries().search(owner_id=["<corporate_id>"]).to_df()
519
+ ```
520
+
521
+ **Key parameters:** `status` (Active/Shut/Unknown/Upcoming), `owner_id`, `operator_id`, `refinery_name`
522
+
523
+ ---
524
+
525
+ ## Worked Examples
526
+
527
+ ### 1. Resolve geography IDs for use in cargo/voyage queries
528
+
529
+ ```python
530
+ from vortexasdk import Geographies
531
+
532
+ targets = {"China": "country", "Singapore": "country", "Rotterdam": "port"}
533
+ ids = {}
534
+ for name, layer in targets.items():
535
+ result = Geographies().search(term=name, exact_term_match=True, filter_layer=layer).to_df()
536
+ if not result.empty:
537
+ ids[name] = result.iloc[0]["id"]
538
+ ```
539
+
540
+ ### 2. Get all crude grades under a category
541
+
542
+ ```python
543
+ from vortexasdk import Products
544
+
545
+ # First find Medium-Sour category
546
+ category = Products().search(term="Medium-Sour", exact_term_match=True, filter_layer="category").to_df()
547
+ parent_id = category.iloc[0]["id"]
548
+
549
+ # Get all grades under it
550
+ grades = Products().search(product_parent=parent_id, filter_layer="grade").to_df()
551
+ ```
552
+
553
+ ### 3. Find all VLCCs owned by a specific company
554
+
555
+ ```python
556
+ from vortexasdk import Corporations, Vessels
557
+
558
+ corp = Corporations().search(term="BAHRI", exact_term_match=True).to_df()
559
+ vlccs = Vessels().search(vessel_classes="oil_vlcc").to_df(columns="all")
560
+ ```
561
+
562
+ ### 4. Look up vessel by IMO number
563
+
564
+ ```python
565
+ from vortexasdk import Vessels
566
+
567
+ df = Vessels().search(ids=[9779836]).to_df(columns=["id", "name", "imo", "mmsi", "dwt", "vessel_class"])
568
+ ```
569
+
570
+ ### 5. Get all active refineries
571
+
572
+ ```python
573
+ from vortexasdk import Refineries
574
+
575
+ df = Refineries().search(status="Active").to_df(columns="all")
576
+ # Filter by country_name in the DataFrame
577
+ ```
578
+
579
+ ### 6. Resolve attribute IDs from vessel data
580
+
581
+ ```python
582
+ from vortexasdk import Attributes
583
+
584
+ propulsion_id = "3ace0e050724707b"
585
+ attr = Attributes().search(ids=[propulsion_id]).to_df()
586
+ # Returns: id, name="DFDE", type="propulsion"
587
+ ```
588
+
589
+ ---
590
+
591
+ ## Error Recovery
592
+
593
+ | Problem | Cause | Fix |
594
+ |---|---|---|
595
+ | Only 1 result returned | Default `size` is 1 | Set `size=500` or higher |
596
+ | No results for a known entity | Name differs from Vortexa label | Try partial name, remove special characters, set `exact_term_match=False` |
597
+ | SDK `Corporations` doesn't filter by entity type | SDK doesn't expose `corporateEntityType` | Use REST API `/v5/reference/charterers` directly |
598
+ | Geography search returns too many results | Common name matches many entries | Add `filter_layer` to narrow results |
599
+ | Vessel search by IMO returns nothing | IMO passed as string instead of integer | Pass IMO as integer in the `ids` or `term` array |
600
+ | Product hierarchy unclear | Don't know which layer a product belongs to | Search without `filter_layer`, check the `layer` field in results |
601
+ | Corporate entity ID doesn't match search | Response uses 16-char truncated IDs | Use the 64-char ID from the entity's `id` field, or search by name |
602
+
603
+ ---
604
+
605
+ ## ID Formats
606
+
607
+ | Format | Length | Used In | Example |
608
+ |---|---|---|---|
609
+ | Full hex ID | 64 characters | All API filter parameters, entity lookups | `a1b2c3d4e5f6...` (64 chars) |
610
+ | Truncated hex ID | 16 characters | `cargo_movement_id` in joins, voyage links, `vessels-ids` with `return_type: "short"` | `a1b2c3d4e5f6a7b8` |
611
+
612
+ **ALWAYS** use full 64-char IDs for API filter parameters. Truncated 16-char IDs appear in cross-endpoint joins (e.g. matching cargo movements to voyages) but are not valid for filter parameters.
613
+
614
+ ---
615
+
616
+ ## Defaults and Pagination
617
+
618
+ | Parameter | Default | Notes |
619
+ |---|---|---|
620
+ | `size` | 1 | **Always set explicitly.** Default returns only 1 record. Max: 10,000 |
621
+ | `search_after` | -- | Use `next_request.search_after` from previous response for pagination |
622
+ | `exact_term_match` (SDK) | `False` | Fuzzy matching by default |
623
+
624
+ **Pagination pattern (REST):**
625
+
626
+ ```python
627
+ import requests
628
+
629
+ all_data = []
630
+ payload = {"term": "crude", "filter_layer": ["grade"], "size": 500}
631
+
632
+ while True:
633
+ resp = requests.post(f"{base}/v5/reference/products", headers=headers, json=payload)
634
+ result = resp.json()
635
+ all_data.extend(result["data"])
636
+ if "next_request" not in result or not result["next_request"]:
637
+ break
638
+ payload["search_after"] = result["next_request"]["search_after"]
639
+ payload["size"] = result["next_request"]["size"]
640
+ ```
641
+
642
+ **Common API response structure:**
643
+
644
+ ```json
645
+ {
646
+ "total": 150,
647
+ "data": [<records>],
648
+ "metadata": [...],
649
+ "next_request": {"size": 500, "search_after": {...}}
650
+ }
651
+ ```