Analyze a fleet in Ghanaian EEZ¶
This guide provides detailed instructions to on how to use the gfw-api-python-client to Monitor a Fleet (a group of vessels) of Tuna Longliners in Ghanaian EEZ region for Compliance using 4Wings API, Vessels API, and Events API.
Note: See the Datasets, Data Caveats, and Terms of Use pages in the GFW API documentation for details on GFW data, API licenses, and rate limits.
Prerequisites¶
Before using the gfw-api-python-client, ensure it is installed (see the Getting Started guide) and that you have obtained an API access token from the Global Fishing Watch API portal.
Installation¶
The gfw-api-python-client can be easily installed using pip:
# %pip install gfw-api-python-client
Usage¶
Import and use gfw-api-python-client in your Python codes
import os
import pandas as pd
import gfwapiclient as gfw
try:
from google.colab import userdata
access_token = userdata.get("GFW_API_ACCESS_TOKEN")
except Exception:
access_token = os.environ.get("GFW_API_ACCESS_TOKEN")
access_token = access_token or "<PASTE_YOUR_GFW_API_ACCESS_TOKEN_HERE>"
gfw_client = gfw.Client(
access_token=access_token,
)
Introduction¶
Use Case: Monitoring a Fleet of Tuna Longliners for Compliance
Kwame is a fisheries compliance officer in Ghana, responsible for monitoring a fleet of tuna longliners operating within Ghanaian Exclusive Economic Zone (EEZ). His goal is to:
Track apparent fishing effort for longliners over the last 12 months.
Identify potential vessels in this fleet, their operational patterns, and their activity levels.
Retrieve vessel details, including flag state, ownership history, and authorizations.
Analyze events such as port visits and encounters (potential transshipment) activities.
APIs Used: ️
4Wings API – Retrieve apparent fishing effort grouped by vessel ID in Ghanaian EEZ.
Vessels API – Get vessel identity, ownership, and compliance details.
Events API – Identify port visits and potential transshipment activities for vessels in the fleet.
Important: In order to avoid any misinterpretation of GFW data, please refer to our official data caveats documentations:
Important Caveats:
The 4Wings API only supports one active report per user at a time.
Sending multiple requests simultaneously results in a 429 Too Many Requests error.
If a report takes over 100 seconds to generate, it may return a 524 Gateway Timeout error.
Step 0: Identify the Region of Interest (ROI) - Ghanaian EEZ¶
Before making API requests, Kwame must specify the geographic area for analysis using a Region ID:
Options to Define the Region:
Using Region ID - Each EEZ has a unique ID in the public-eez-areas dataset.
Custom Geometries - Users can define a custom area using GeoJSON.
For Ghanaian EEZ, the region ID is 8400 (public-eez-areas dataset).
Step 1: Retrieve Fishing Effort in Ghanaian EEZ¶
Kwame first queries the 4Wings API to get fishing effort for all vessels, grouping them by gear type in Ghanaian EEZ. Please learn more about apparent fishing effort here and check its data caveats here.
Filters Used:
Region ID - 8400 Ghanaian EEZ
Date Range - Last 12 Months
Grouped By - Gear Type
step_1_report_result = await gfw_client.fourwings.create_fishing_effort_report(
spatial_resolution="LOW",
group_by="GEARTYPE",
temporal_resolution="ENTIRE",
start_date="2024-01-01",
end_date="2025-01-01",
spatial_aggregation=True,
region={
"dataset": "public-eez-areas",
"id": "8400",
},
)
step_1_report_df = step_1_report_result.df()
step_1_report_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9 entries, 0 to 8
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 9 non-null object
1 detections 0 non-null object
2 flag 0 non-null object
3 gear_type 9 non-null object
4 hours 9 non-null float64
5 vessel_ids 9 non-null int64
6 vessel_id 0 non-null object
7 vessel_type 0 non-null object
8 entry_timestamp 0 non-null object
9 exit_timestamp 0 non-null object
10 first_transmission_date 0 non-null object
11 last_transmission_date 0 non-null object
12 imo 0 non-null object
13 mmsi 0 non-null object
14 call_sign 0 non-null object
15 dataset 0 non-null object
16 report_dataset 9 non-null object
17 ship_name 0 non-null object
18 lat 0 non-null object
19 lon 0 non-null object
dtypes: float64(1), int64(1), object(18)
memory usage: 1.5+ KB
step_1_report_df[["flag", "gear_type", "hours", "vessel_ids"]].head()
| flag | gear_type | hours | vessel_ids | |
|---|---|---|---|---|
| 0 | None | purse_seines | 6.340556 | 1 |
| 1 | None | drifting_longlines | 593.138333 | 3 |
| 2 | None | other_purse_seines | 26.581111 | 1 |
| 3 | None | fishing | 20929.563333 | 21 |
| 4 | None | inconclusive | 30496.173611 | 27 |
step_1_agg_report_df = (
step_1_report_df.groupby(["gear_type"], as_index=False)
.agg(hours=("hours", "sum"), vessel_ids=("vessel_ids", "sum"))
.sort_values(by="hours", ascending=False)
)
step_1_agg_report_df
| gear_type | hours | vessel_ids | |
|---|---|---|---|
| 7 | trawlers | 46704.797222 | 26 |
| 3 | inconclusive | 30496.173611 | 27 |
| 1 | fishing | 20929.563333 | 21 |
| 8 | tuna_purse_seines | 5146.623889 | 23 |
| 5 | pole_and_line | 3481.509167 | 5 |
| 0 | drifting_longlines | 593.138333 | 3 |
| 4 | other_purse_seines | 26.581111 | 1 |
| 6 | purse_seines | 6.340556 | 1 |
| 2 | fixed_gear | 0.163611 | 1 |
What We have Learned from Step 1¶
Kwame now has apparent fishing effort data for multiple gear types.
There are potential 3 vessels operating as a longliners (i.e., drifting_longlines) in Ghanaian EEZ with
593.138333 hourslogged.
Step 2: Retrieve Vessel IDs for Longliners¶
Kwame refines his 4Wings API request to group by vessel ID, and filtering only for longliners in Ghanaian EEZ.
Filters Used:
Date Range - Last 12 Months
Grouped By - Vessel ID
Why Use group-by=VESSEL_ID?
Grouping by VESSEL_ID allows individual vessel identification in the response. This is crucial for tracking vessel activity and, more importantly, linking each detected vessel to the Vessels API in the next step. By structuring the query this way, we can fetch vessel details such as flag, name, and ownership records in Step 3 below.
step_2_report_result = await gfw_client.fourwings.create_fishing_effort_report(
spatial_resolution="LOW",
group_by="VESSEL_ID",
temporal_resolution="ENTIRE",
filters=["geartype in ('drifting_longlines')"],
start_date="2024-01-01",
end_date="2025-01-01",
spatial_aggregation=True,
region={
"dataset": "public-eez-areas",
"id": "8400",
},
)
step_2_report_df = step_2_report_result.df()
step_2_report_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date 3 non-null object
1 detections 0 non-null object
2 flag 3 non-null object
3 gear_type 3 non-null object
4 hours 3 non-null float64
5 vessel_ids 0 non-null object
6 vessel_id 3 non-null object
7 vessel_type 3 non-null object
8 entry_timestamp 3 non-null datetime64[ns, UTC]
9 exit_timestamp 3 non-null datetime64[ns, UTC]
10 first_transmission_date 3 non-null datetime64[ns, UTC]
11 last_transmission_date 3 non-null datetime64[ns, UTC]
12 imo 3 non-null object
13 mmsi 3 non-null object
14 call_sign 3 non-null object
15 dataset 3 non-null object
16 report_dataset 3 non-null object
17 ship_name 3 non-null object
18 lat 0 non-null object
19 lon 0 non-null object
dtypes: datetime64[ns, UTC](4), float64(1), object(15)
memory usage: 612.0+ bytes
step_2_report_df[["flag", "gear_type", "hours", "mmsi", "ship_name"]].head()
| flag | gear_type | hours | mmsi | ship_name | |
|---|---|---|---|---|---|
| 0 | CHN | DRIFTING_LONGLINES | 2.750556 | 412331032 | |
| 1 | JPN | DRIFTING_LONGLINES | 588.879722 | 431100690 | SENSHU MARU NO.3 |
| 2 | TWN | DRIFTING_LONGLINES | 1.508056 | 416007496 | HUNG CHUAN SHUN |
What We have Learned from Step 2¶
Kwame identifies
3 vesselsoperating as longliners within Ghanaian EEZ.The vessel
(mmsi: 431100690, ship_name: SENSHU MARU NO.3)shows significant activity with588.879722 hourslogged.Other vessels
(mmsi: 416007496, ship_name: HUNG CHUAN SHUN)and(mmsi: 412331032)shows apparent fishing effort over a short duration.This response is based on AIS self-reported data and should be further validated.
Step 3: Retrieve Vessel Details Using the Vessels API¶
Kwame queries the Vessels API to get detailed vessel identity and ownership records. Please learn more about Vessels API here and check its data caveats here.
Filters Used:
Vessel IDs from 4Wings API, Step 2 above.
Datasets -
public-global-vessel-identity:latest.Includes -
POTENTIAL_RELATED_SELF_REPORTED_INFO.
Note: Vessels may change identifiers over time, such as their Maritime Mobile Service Identity (MMSI), International Maritime Organization (IMO) number), call sign, or even their name. These changes can occur due to re-registration, changes in ownership, or other operational reasons within the AIS transponder. Parameter (includes = POTENTIAL_RELATED_SELF_REPORTED_INFO) helps group all vessel ids that are potentially related as part of the same physical vessel based on publicly available registry information.
step_2_vessel_ids = list(step_2_report_df["vessel_id"].unique())
step_2_vessel_ids
['f37ebdc1b-be44-0740-7904-49397360e29d',
'b1dad8628-8c9c-2ee7-258b-3d8fb747f1c8',
'60f7bb972-2c90-4553-650b-23c38f9521bf']
step_3_vessels_result = await gfw_client.vessels.get_vessels_by_ids(
ids=step_2_vessel_ids,
)
step_3_vessels_df = step_3_vessels_result.df()
step_3_vessels_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 dataset 3 non-null object
1 registry_info_total_records 3 non-null int64
2 registry_info 3 non-null object
3 registry_owners 3 non-null object
4 registry_public_authorizations 3 non-null object
5 combined_sources_info 3 non-null object
6 self_reported_info 3 non-null object
dtypes: int64(1), object(6)
memory usage: 300.0+ bytes
Understanding Vessel Details Response Data
registryInfoTotalRecords – This represents the number of registry records found for the vessels.
registryInfo – Contains public registry data. This data is sourced from official vessel registries.
registryOwners – Lists the registered owners of the vessel based on public sources.
registryPublicAuthorizations – Represents known fishing authorizations from public sources. Users should verify against national registries and RFMO records for additional context.
combinedSourcesInfo – Provides inferred data from multiple sources, including. This is not explicitly reported by vessels but determined through GFW’s classification methods.
selfReportedInfo – Contains AIS self-reported data, including
MMSI,ship name, andflagas broadcast by the vessel itself. Self-reported data may not always align with registry data and should be cross-checked.
step_3_vessels_df[["registry_info", "registry_owners", "self_reported_info"]]
| registry_info | registry_owners | self_reported_info | |
|---|---|---|---|
| 0 | [{'id': '02eda7d2da02943eecd48813fb7d562a', 's... | [{'name': 'HER RONG SHUN FISHERIES', 'flag': '... | [{'id': '60f7bb972-2c90-4553-650b-23c38f9521bf... |
| 1 | [{'id': '0b0dec977c1aad4ae6652c4076572cc7', 's... | [{'name': 'TOMIOKA FISHERIES', 'flag': 'JPN', ... | [{'id': 'c86d138c5-5d51-3620-fe01-a9e0ed37fb91... |
| 2 | [] | [] | [{'id': 'f37ebdc1b-be44-0740-7904-49397360e29d... |
Explore Vessels Registry Info¶
step_3_registry_info_df = pd.json_normalize(
step_3_vessels_df["registry_info"].explode()
)
step_3_registry_info_df = step_3_registry_info_df.dropna()
step_3_registry_info_df[
["ssvid", "flag", "ship_name", "n_ship_name", "gear_types", "source_code"]
]
| ssvid | flag | ship_name | n_ship_name | gear_types | source_code | |
|---|---|---|---|---|---|---|
| 0 | 416007496 | TWN | HUNG CHUAN SHUN | HUNGCHUANSHUN | [DRIFTING_LONGLINES] | [ICCAT, IMO, ISSF, OPRT, TMT_ICCAT, TMT_OTHER_... |
| 1 | 431100690 | JPN | SENSHU MARU NO.3 | SENSHUMARU3 | [DRIFTING_LONGLINES] | [CCSBT, GFCM, IATTC, ICCAT, IMO, IOTC, OPRT, R... |
Explore Registry Owners¶
step_3_registry_owners_df = pd.json_normalize(
step_3_vessels_df["registry_owners"].explode()
)
step_3_registry_owners_df = step_3_registry_owners_df.dropna()
step_3_registry_owners_df[["ssvid", "flag", "name", "source_code"]]
| ssvid | flag | name | source_code | |
|---|---|---|---|---|
| 0 | 416007496 | TWN | HER RONG SHUN FISHERIES | [TMT_ICCAT, TMT_OTHER_OFFICIAL] |
| 1 | 431100690 | JPN | TOMIOKA FISHERIES | [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF... |
| 2 | 431100690 | JPN | TOMIOKA | [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF... |
| 3 | 431100690 | JPN | YAMAMOTO YUUKI | [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF... |
| 4 | 431100690 | JPN | YAMAMOTO HIROKI | [TMT_CCSBT, TMT_IATTC, TMT_ICCAT, TMT_OTHER_OF... |
Explore Vessels Self Reported Info¶
step_3_self_reported_info_df = pd.json_normalize(
step_3_vessels_df["self_reported_info"].explode()
)
step_3_self_reported_info_df[
["ssvid", "flag", "ship_name", "n_ship_name", "source_code"]
]
| ssvid | flag | ship_name | n_ship_name | source_code | |
|---|---|---|---|---|---|
| 0 | 416007496 | TWN | HUNG CHUAN SHUN | HUNGCHUANSHUN | [AIS] |
| 1 | 431100690 | JPN | None | None | [AIS] |
| 2 | 431100690 | JPN | SENSHU MARU NO.3 | SENSHUMARU3 | [AIS] |
| 3 | 431100690 | JPN | SENSHU MARU NO3 | SENSHUMARU3 | [AIS] |
| 4 | 412331032 | CHN | None | None | [AIS] |
What We have Learned from Step 3¶
The vessel
mmsi/ssvid: 412331032appears to be a drifting longliner flagged under China.No public registry data is found for this vessel.
The vessel’s identity information is based on
AIS self-reported data, which may not always align with official registries.The vessel appears to have been active since
2014, based onself-reported AIS records.This vessel’s data needs further validation against official public sources
Step 4: Detect Fleet Activity (Port Visits)¶
Now that Kwame has identified vessels in the fleet, he examines their activity further by querying the Events API. This allows him to detect port visits, encounters (Potential Transshipment) and apparent fishing activity based on vessel movement patterns. Please learn more about Events API here and check its data caveats here.
Filters Used:
Vessel ID from 4Wings API
Event Types - Port visits, encounters (potential transshipment), and fishing events.
Time Range - Last 6 months.
-
public-global-port-visits-events::latest(Port Visits)public-global-encounters-events:latest(Encounters between vessels)public-global-fishing-events:latest(Fishing activity)
Encounter Types - FISHING-FISHING
step_4_events_result = await gfw_client.events.get_all_events(
datasets=[
"public-global-encounters-events:latest",
"public-global-fishing-events:latest",
"public-global-port-visits-events:latest",
],
vessels=step_2_vessel_ids,
types=["ENCOUNTER", "FISHING", "PORT_VISIT"],
start_date="2024-08-01",
end_date="2025-01-31",
encounter_types=["FISHING-FISHING"],
)
step_4_events_df = step_4_events_result.df()
step_4_events_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 start 418 non-null datetime64[ns, UTC]
1 end 418 non-null datetime64[ns, UTC]
2 id 418 non-null object
3 type 418 non-null object
4 position 418 non-null object
5 regions 418 non-null object
6 bounding_box 418 non-null object
7 distances 418 non-null object
8 vessel 418 non-null object
9 encounter 0 non-null object
10 fishing 414 non-null object
11 gap 0 non-null object
12 loitering 0 non-null object
13 port_visit 4 non-null object
dtypes: datetime64[ns, UTC](2), object(12)
memory usage: 45.8+ KB
step_4_events_df["type"].value_counts()
type
fishing 414
port_visit 4
Name: count, dtype: int64
Explore Apparent Fishing Events¶
step_4_fishing_events_df = step_4_events_df[step_4_events_df["fishing"].notna()]
step_4_fishing_df = pd.concat(
[
pd.json_normalize(step_4_fishing_events_df["vessel"], sep="_"),
pd.json_normalize(step_4_fishing_events_df["fishing"], sep="_"),
],
axis=1,
)
step_4_fishing_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 414 entries, 0 to 413
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 414 non-null object
1 name 389 non-null object
2 ssvid 414 non-null object
3 flag 414 non-null object
4 type 414 non-null object
5 public_authorizations 414 non-null object
6 nextPort 0 non-null object
7 total_distance_km 414 non-null float64
8 average_speed_knots 414 non-null float64
9 average_duration_hours 0 non-null object
10 potential_risk 414 non-null bool
11 vessel_public_authorization_status 414 non-null object
dtypes: bool(1), float64(2), object(9)
memory usage: 36.1+ KB
step_4_fishing_df[
[
"name",
"ssvid",
"total_distance_km",
"average_speed_knots",
]
]
| name | ssvid | total_distance_km | average_speed_knots | |
|---|---|---|---|---|
| 0 | HUNG CHUAN SHUN | 416007496 | 27.524249 | 8.183333 |
| 1 | HUNG CHUAN SHUN | 416007496 | 11.016940 | 6.285714 |
| 2 | HUNG CHUAN SHUN | 416007496 | 88.156534 | 4.255172 |
| 3 | HUNG CHUAN SHUN | 416007496 | 143.741059 | 5.758621 |
| 4 | HUNG CHUAN SHUN | 416007496 | 129.531733 | 4.850420 |
| ... | ... | ... | ... | ... |
| 409 | HUNG CHUAN SHUN | 416007496 | 68.003416 | 5.202439 |
| 410 | HUNG CHUAN SHUN | 416007496 | 62.649188 | 4.577143 |
| 411 | HUNG CHUAN SHUN | 416007496 | 85.981416 | 4.420408 |
| 412 | HUNG CHUAN SHUN | 416007496 | 74.555684 | 3.927660 |
| 413 | SENSHU MARU NO.3 | 431100690 | 12.164494 | 5.083333 |
414 rows × 4 columns
step_4_fishing_df["ssvid"].value_counts()
ssvid
416007496 363
431100690 26
412331032 25
Name: count, dtype: int64
Explore Port Visit Events¶
step_4_port_visit_events_df = step_4_events_df[step_4_events_df["port_visit"].notna()]
step_4_port_visits_df = pd.concat(
[
pd.json_normalize(step_4_port_visit_events_df["vessel"], sep="_"),
pd.json_normalize(step_4_port_visit_events_df["port_visit"], sep="_"),
],
axis=1,
)
step_4_port_visits_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4 entries, 0 to 3
Data columns (total 37 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 4 non-null object
1 name 3 non-null object
2 ssvid 4 non-null object
3 flag 4 non-null object
4 type 4 non-null object
5 public_authorizations 4 non-null object
6 nextPort 0 non-null object
7 visit_id 4 non-null object
8 confidence 4 non-null object
9 duration_hrs 4 non-null float64
10 start_anchorage_anchorage_id 4 non-null object
11 start_anchorage_at_dock 4 non-null bool
12 start_anchorage_distance_from_shore_km 4 non-null float64
13 start_anchorage_flag 4 non-null object
14 start_anchorage_id 4 non-null object
15 start_anchorage_lat 4 non-null float64
16 start_anchorage_lon 4 non-null float64
17 start_anchorage_name 4 non-null object
18 start_anchorage_top_destination 4 non-null object
19 intermediate_anchorage_anchorage_id 4 non-null object
20 intermediate_anchorage_at_dock 4 non-null bool
21 intermediate_anchorage_distance_from_shore_km 4 non-null float64
22 intermediate_anchorage_flag 4 non-null object
23 intermediate_anchorage_id 4 non-null object
24 intermediate_anchorage_lat 4 non-null float64
25 intermediate_anchorage_lon 4 non-null float64
26 intermediate_anchorage_name 4 non-null object
27 intermediate_anchorage_top_destination 4 non-null object
28 end_anchorage_anchorage_id 4 non-null object
29 end_anchorage_at_dock 4 non-null bool
30 end_anchorage_distance_from_shore_km 4 non-null float64
31 end_anchorage_flag 4 non-null object
32 end_anchorage_id 4 non-null object
33 end_anchorage_lat 4 non-null float64
34 end_anchorage_lon 4 non-null float64
35 end_anchorage_name 4 non-null object
36 end_anchorage_top_destination 4 non-null object
dtypes: bool(3), float64(10), object(24)
memory usage: 1.2+ KB
step_4_port_visits_df[
[
"name",
"ssvid",
"confidence",
"start_anchorage_name",
"intermediate_anchorage_name",
"end_anchorage_name",
]
]
| name | ssvid | confidence | start_anchorage_name | intermediate_anchorage_name | end_anchorage_name | |
|---|---|---|---|---|---|---|
| 0 | SENSHU MARU NO.3 | 431100690 | 4 | TEMA | TEMA | TEMA |
| 1 | None | 412331032 | 4 | DAKAR | DAKAR | DAKAR |
| 2 | HUNG CHUAN SHUN | 416007496 | 4 | TEMA | TEMA | TEMA |
| 3 | SENSHU MARU NO.3 | 431100690 | 4 | TEMA | TEMA | TEMA |
What We’ve Learned from Step 4¶
4: port visits,0: encounters, and414: fishingevents were found for the queried vessels in the given date range.Some events were missed due to AIS data coverage gaps.
Different filters may need to be applied to refine results.
Caveats & Considerations
A lack of recorded encounters or any other events does not confirm the absence of such activities—AIS coverage, reporting behavior, and dataset updates can impact results.
Further investigation may be required, including manual validation using historical data or consulting additional sources.
Summary of API Flow¶
4Wings API - Identify fishing effort by gear type in Ghanaian EEZ.
4Wings API - Retrieve vessel IDs for potential longliners.
Vessels API - Fetch detailed vessel identity & ownership.
Events API - Attempt to detect fleet activity (port visits, encounters, and apparent fishing events)