Allocation patterns, partner concentration, grant scale and the health sector
Author
Jesus Baena
Published
April 24, 2026
1 Introduction
This report analyses the Ukraine Humanitarian Pooled Fund (UHF) from its first allocation in 2019 through 2025, and situates it within the broader CBPF system using comparative data from 28 country-based pooled funds over 2014–2025 (the mature operational period of the modality).
Rather than following the linear logic of the project cycle (number of beneficiaries, project duration, funding executed, etc.), this report steps back to examine overall funding flows and grant characteristics, with particular attention to the health sector.
The analysis is structured around three key questions:
What is the profile of the organizations receiving UHF funding? How does it compare with the rest of the CBPF system? What are the implications for the health sector?
1.1 Key Findings
Heavy reliance on the reserve allocation: Since Ukraine became an active CBPF in 2019, Reserve allocations have accounted for 51.9 % of its budget (2019–2025) — well above the non-Ukraine CBPF average of 40.2 % — placing Ukraine among the top tier of funds (7th of 28) in reserve dependence, alongside Sudan, oPt and DRC. The balance is correcting in 2025.
Larger grants, not fewer partners: Ukraine grants are 2.6× larger at the median than in the rest of the CBPF system ($1.13M vs $440K). This concentration of associates — rather than a narrower partner base — is a defining feature of the UHF portfolio.
Same shape × bigger envelope = bigger grants: In general numbers, UHF is not more concentrated than peer CBPFs. Across 2022–2024 Ukraine sits mid-pack among broad-partnership CBPFs on HHI (0.029), top-5 share (28%) and top-10 share (46%). What sets UHF apart is envelope size: $541M spread across 94 partners produces a mean partner take of $5.75M over three years — roughly 2× Yemen or Ethiopia and 4× Somalia.
A stable core-20 captures 55% of the envelope: Twenty organisations appeared in every UHF allocation round 2022–2024 and absorbed $299M of the $541M envelope. Seven of them (ACTED, UNHCR, DRC, PIN, Caritas Ukraine, NRC, Proliska) hold mean grants of $3.4M–$4.8M — at or near the $5M cap — with individual exceptional-cap grants up to $9.4M.
Ceiling-bound distribution: UHF’s P90 grant size is exactly $5.00M (the standard cap) — unique among CBPFs. Peer funds show a long tail; UHF shows a flat top.
Visible capacity-volume mismatches: Where 2021 revenue is public, some organizations has managed UHF volumes 4.3× its pre-war annual revenue ($9.4M against €1.9M). Seven of the eight core Ukrainian NNGOs have no publicly available 2021 revenue — an independent-assessment gap rather than a finding.
Structural UN exit: UN-Agency funding collapsed from $66M (2022) to $41M (2023) to $5M (2024); the 2024 Annual Report makes this explicit (“UN would no longer be funded unless absolutely necessary”). Redistributed volume flowed primarily to DRC, ICF Caritas Ukraine and a broadened NNGO tier. Probably also driven by a sufficient multilateral funding portfolio.
UHF-in-context: AR-reported, Ukraine = 20% of global CBPF allocations (2024) and >10% of the 2024 HNRP — the largest CBPF for the third consecutive year.
Data Sources: Official UHF allocation and project data (CBPF Feb 2026 extract); UHF Annual Reports 2022, 2023, 2024.
2 Data Overview
Data is retrieved from the official repository data explorer, it was downloaded on February 2026 and it is formed by raw .csv files. The raw data spans from 2010 onward, but analysis focuses on 2014-2025 to ensure comparability (2010-2013 was the pilot phase with limited geographic coverage). Data was cut at the end of 2025 to use static data for this analysis. As such, the recently announced top-up allocation for Ukraine from the US State Department in February 2026 is not included in this report. A posteriour update could be considered.
In total the data comprises:
28 country-based pooled funds (one per PooledFundName, including two dedicated Regional Humanitarian Pooled Funds — Chad RhPF and Mozambique RhPF — and “Syria Cross-border” alongside Syria)
16,304 approved projects across all years; 15,657 within the 2014–2025 analysis window
891 distinct allocation rounds (AllocationType), e.g. “2022 3rd Reserve Allocation”, “2024 1st Standard Allocation”
Show code
# Load all data files - Latest download from February 19, 2026# Using complete global dataset (ALL versions) for all 28 CBPF countriesproject_summary<-read_csv("Raw-data/ProjectSummary_ALL_20260219_100323_UTC.csv", show_col_types =FALSE)project_summary_location<-read_csv("Raw-data/ProjectSummaryWithLocation_ALL_20260219_100323_UTC.csv", show_col_types =FALSE)pipeline_summary<-read_csv("Raw-data/PipelineProjectSummary_ALL_20260219_125012_UTC.csv", show_col_types =FALSE)pipeline_cluster<-read_csv("Raw-data/PipelineProjectCluster_ALL_20260219_100323_UTC.csv", show_col_types =FALSE)clusters<-read_csv("Raw-data/Cluster_ALL_20260219_100323_UTC.csv", show_col_types =FALSE)contributions<-read_csv("Raw-data/Contribution_ALL_20260219_100323_UTC.csv", show_col_types =FALSE)
2.1 Data Preparation
The raw data is well-structured and consistent with expected values. Minor transformations were applied to standardise date formats across datasets and to filter out 2026 entries, keeping the analysis within the 2014–2025 window. Helper functions safe_date_format and safe_year_format handle edge cases in date parsing robustly.
Data Preparation Details
Date conversions
Dataset
Date columns converted
ProjectSummary
ProjectStartDate, ProjectEndDate → mdy_hms()
ProjectSummaryWithLocation
ActualStartDate, ActualEndDate → mdy_hms()
PipelineProjectSummary
ActualStartDate, ActualEndDate → mdy_hms()
pipeline_cluster, clusters
Use numeric AllocationYear — no conversion needed
contributions
PledgeDate parsed on demand during analysis
Temporal cutoff — analysis endpoint: December 31, 2025
AllocationYear != 2026 applied to: project_summary, project_summary_location, pipeline_summary, pipeline_cluster
FiscalYear != 2026 applied to: contributions
Helper functions
safe_date_format() — returns the min/max of a date vector, filtered to the 2000–2025 range; returns "N/A" if no valid dates remain
safe_year_format() — same logic for numeric year vectors; returns the result as a character string
Show code
# ============================================================# PART 1: Date and Type Conversions# ============================================================# Parse date columns for project datasetsproject_summary<-project_summary%>%mutate( ProjectStartDate =mdy_hms(ProjectStartDate), ProjectEndDate =mdy_hms(ProjectEndDate))# Note: project_summary_location and pipeline_summary use ActualStartDateproject_summary_location<-project_summary_location%>%mutate( ActualStartDate =mdy_hms(ActualStartDate), ActualEndDate =mdy_hms(ActualEndDate))pipeline_summary<-pipeline_summary%>%mutate( ActualStartDate =mdy_hms(ActualStartDate), ActualEndDate =mdy_hms(ActualEndDate))# Note: pipeline_cluster and clusters use AllocationYear (numeric)# Note: contributions uses PledgeDate (will be parsed when needed)# ============================================================# PART 2: Filter Out 2026 Data# ============================================================# Remove all data from 2026 to maintain analysis cutoff at end of 2025project_summary<-project_summary%>%filter(AllocationYear!=2026)project_summary_location<-project_summary_location%>%filter(AllocationYear!=2026)pipeline_summary<-pipeline_summary%>%filter(AllocationYear!=2026)pipeline_cluster<-pipeline_cluster%>%filter(AllocationYear!=2026)contributions<-contributions%>%filter(FiscalYear!=2026)# ============================================================# PART 3: Helper Functions# ============================================================# Safely format date ranges, filtering invalid datessafe_date_format<-function(date_vec, func=min){valid_dates<-date_vec[!is.na(date_vec)&year(date_vec)>=2000&year(date_vec)<=2025]if(length(valid_dates)==0)return("N/A")result<-func(valid_dates)return(format(result, "%B %d, %Y"))}# Format year ranges for datasets that use AllocationYearsafe_year_format<-function(year_vec, func=min){valid_years<-year_vec[!is.na(year_vec)&year_vec>=2000&year_vec<=2025]if(length(valid_years)==0)return("N/A")return(as.character(func(valid_years)))}
The data site offers an endpoint to retrieve projects on the pipeline (under review, cancelled, etc…). After multiple attempts to retrieve comprehensive pipeline data from the CBPF API, it was confirmed that the PipelineProjectSummary endpoint returns only 581 entries from scattered years (2013-2015, 2022, 2025-2026), representing just a fraction of all project submissions across the CBPF system’s history, and with inconsistencies (including an anomalous pre-2010 entry predating the formal CBPF system).
Ukraine-specific: Only 1 entry from 2022 allocation (out of 413 approved Ukraine projects (2019–2025) in ProjectSummary)
Cannot calculate meaningful approval/rejection rates: Missing data prevents comprehensive submission vs approval analysis
What this means: - We cannot analyze Ukraine’s proposal-to-approval ratio - We cannot determine how many Ukraine projects were rejected or cancelled - We cannot compare Ukraine’s approval success rate to other pooled funds - Analysis must focus on approved projects from the ProjectSummary dataset
3 Global summary
OCHA has operated country-based pooled funds in more than 40 countries, with some locations hosting separate cross-border or regional mechanisms. The dataset used here covers the 28 pooled funds active in the 2010–2025 period, including the Syria Cross-border mechanism and two Regional Humanitarian Pooled Funds (Chad RhPF, Mozambique RhPF).
The data shows the humanitarian gap visible, in recent years, the number of countries receiving funding has increased, but the total budget allocated has gone in the wrong direction.
Show code
# Aggregate data by year - separate Ukraine from other countries# Keep all years (2010-2025) to show pilot phase vs mature operational periodglobal_summary_detailed<-project_summary%>%filter(!is.na(AllocationYear), !is.na(Budget))%>%mutate(Country_Group =if_else(PooledFundName=="Ukraine", "Ukraine", "Other Countries"))%>%group_by(AllocationYear, Country_Group)%>%summarise(Budget =sum(Budget, na.rm =TRUE), .groups ='drop')%>%arrange(AllocationYear, Country_Group)# Get total budget by year for labelsglobal_totals<-global_summary_detailed%>%group_by(AllocationYear)%>%summarise( Total_Budget =sum(Budget), .groups ='drop')# Get country count by year for the linecountry_counts<-project_summary%>%filter(!is.na(AllocationYear))%>%group_by(AllocationYear)%>%summarise(Countries =n_distinct(PooledFundName), .groups ='drop')# Merge totals with country countsglobal_summary<-global_totals%>%left_join(country_counts, by ="AllocationYear")# Create scaling factor for secondary axisscale_factor<-max(global_summary$Total_Budget)/max(global_summary$Countries)/1e6# Create combination chart with Ukraine highlightedggplot()+# Stacked bars for budget (Ukraine in yellow, others in teal)geom_col(data =global_summary_detailed,aes(x =as.factor(AllocationYear), y =Budget/1e6, fill =Country_Group), alpha =0.8, width =0.7)+# Line for countriesgeom_line(data =global_summary,aes(x =as.factor(AllocationYear), y =Countries*scale_factor, group =1), color ="#e63946", linewidth =1.2)+geom_point(data =global_summary,aes(x =as.factor(AllocationYear), y =Countries*scale_factor), color ="#e63946", size =3)+# Labels at top of barsgeom_text(data =global_summary,aes(x =as.factor(AllocationYear), y =Total_Budget/1e6, label =paste0("$", round(Total_Budget/1e6, 0), "M")), vjust =-0.5, size =3.5, fontface ="bold", color ="#333333")+# Color scalescale_fill_manual( values =c("Ukraine"="#f4a261", "Other Countries"="#005f73"), name ="Region")+# Primary y-axisscale_y_continuous( name ="Total Budget Allocated (Million USD)", labels =scales::comma, limits =c(0, NA), expand =expansion(mult =c(0, 0.1)),# Secondary y-axis sec.axis =sec_axis(~./scale_factor, name ="Number of Countries", breaks =scales::pretty_breaks(n =6)))+labs( title ="Global CBPF Funding Allocation and Country Coverage by Year (2010-2025)", subtitle ="Budget shown in bars (Ukraine highlighted in yellow), country count shown as line • Pilot phase (2010-2013) shows limited scale", x ="Allocation Year")+theme_minimal()+theme( plot.title =element_text(face ="bold", size =16), plot.subtitle =element_text(size =12, color ="#666666"), axis.title.y.left =element_text(color ="#005f73", face ="bold", size =12), axis.title.y.right =element_text(color ="#e63946", face ="bold", size =12), axis.text =element_text(size =11), axis.text.x =element_text(angle =45, hjust =1), panel.grid.minor =element_blank(), legend.position ="bottom", legend.title =element_text(face ="bold"))
The 2010-2013 period represents the early pilot phase of CBPF with limited geographic coverage (primarily one country) and significantly lower funding levels. A major scale-up occurred in 2014 when the system expanded to multiple countries and reached operational maturity. To ensure data comparability, subsequent analysis focuses on the mature operational period from 2014 to 2025.
3.1 Budget Distribution by Country (2022-2025)
This visualization shows how CBPF funding was distributed across countries for each year from 2022 to 2025. Ukraine is highlighted in yellow to track its position among the top recipients throughout this period.
Show code
# Get top countries per yearcountry_budget_yearly<-project_summary%>%filter(AllocationYear>=2022, AllocationYear<=2025)%>%filter(!is.na(Budget), !is.na(PooledFundName))%>%group_by(AllocationYear, PooledFundName)%>%summarise( Total_Budget =sum(Budget, na.rm =TRUE), Projects =n(), .groups ='drop')# Get top 12 countries per year (or include Ukraine if not in top 12)top_countries_per_year<-country_budget_yearly%>%group_by(AllocationYear)%>%arrange(desc(Total_Budget))%>%slice_head(n =12)%>%ungroup()# Ensure Ukraine is included in all yearsukraine_data<-country_budget_yearly%>%filter(PooledFundName=="Ukraine")# Combine and remove duplicatescountry_budget_yearly_filtered<-bind_rows(top_countries_per_year, ukraine_data)%>%distinct(AllocationYear, PooledFundName, .keep_all =TRUE)# Add highlighting, labels, and ordering within each yearcountry_budget_yearly_filtered<-country_budget_yearly_filtered%>%mutate( Highlight =if_else(PooledFundName=="Ukraine", "Ukraine", "Other"))%>%group_by(AllocationYear)%>%mutate( Rank =rank(-Total_Budget), Label =if_else(Rank<=8|PooledFundName=="Ukraine",paste0("$", round(Total_Budget/1e6, 0), "M"),""))%>%ungroup()%>%# Create a compound key for proper ordering within facetsmutate( Year_Country =paste(AllocationYear, PooledFundName, sep ="_"), Year_Country =reorder(Year_Country, Total_Budget))# Create faceted horizontal bar chartggplot(country_budget_yearly_filtered, aes(x =Total_Budget/1e6, y =Year_Country))+geom_col(aes(fill =Highlight), alpha =0.85, width =0.75)+geom_text(aes(label =Label), hjust =-0.1, size =3, fontface ="bold", color ="#333333")+scale_fill_manual( values =c("Ukraine"="#f4a261", "Other"="#005f73"), guide ="none")+scale_y_discrete(labels =function(x)gsub("^\\d{4}_", "", x))+scale_x_continuous( labels =scales::comma, expand =expansion(mult =c(0, 0.15)))+facet_wrap(~AllocationYear, ncol =2, scales ="free_y")+labs( title ="Top Countries by CBPF Budget Allocation per Year (2022-2025)", subtitle ="Ukraine highlighted in yellow • Shows top recipients each year and how rankings shift", x ="Budget (Million USD)", y =NULL)+theme_minimal()+theme( plot.title =element_text(face ="bold", size =16, margin =margin(b =5)), plot.subtitle =element_text(size =11, color ="#666666", margin =margin(b =15)), axis.text.y =element_text(size =9.5), axis.text.x =element_text(size =9), axis.title.x =element_text(face ="bold", size =11, margin =margin(t =10)), strip.text =element_text(face ="bold", size =13, margin =margin(b =10)), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank(), panel.spacing =unit(1.5, "lines"), plot.margin =margin(15, 15, 15, 15))
Afghanistan is the only country that has received more funding than Ukraine in 2022, this is due to commitments made at the global level following the Taliban takeover, probably made earlier than February 2022 as contributions to the appeal.
Also note, that this cannot be taken as an indicator of the global humanitarian attention, CBPFs is a flexible global mechanism that can balance the funding situation between different crises, considering the attention given by other donors.
4 Allocation Flow Analysis
Country-Based Pool Funds have the two ways of publish allocations: the standard allocation and the reserve allocation.
Standard Allocation: Regular funding cycles based on strategic response plans and humanitarian response planning
Reserve Allocation: Emergency funding for urgent needs, rapid response to sudden onset crises, and critical gaps
Key Differences from Standard Allocations:
Speed: The timeline is compressed. A Reserve Allocation can be processed from launch to signature in as little as 48-72 hours in extreme cases, though 2-3 weeks is more common.
Targeting: The strategy is highly prescriptive. The HC may define not just the geographic area but the specific activities required (e.g., “Provision of emergency shelter kits in District X”).
Partner Selection: While Standard Allocations are open calls, Reserve Allocations can be “closed” or restricted. The HC may invite specific partners who are known to have presence and capacity in the affected area to submit proposals, thereby bypassing the open competitive process to save time.
4.1 Budget Distribution by Allocation Source
UHF is among the heaviest users of the reserve allocation in the CBPF system, with Reserve accounting for 51.9 % of its 2019–2025 budget — well above the non-Ukraine average of 40.2 % and placing Ukraine 7th of 28 funds on this metric (alongside Sudan, oPt and DRC). This chart compares the distribution of standard vs reserve allocations for Ukraine against the rest of the world, showing that while Ukraine has a higher reliance on reserve allocations, there is a trend towards more balanced funding in 2025.
Show code
# Ukraine allocation summaryukraine_allocation_summary<-project_summary%>%filter(AllocationYear>=2014, !is.na(AllocationSourceName),PooledFundName=="Ukraine")%>%group_by(AllocationSourceName)%>%summarise( Projects =n(), Budget =sum(Budget, na.rm =TRUE), .groups ='drop')%>%mutate( Region ="Ukraine",# Force factor ordering: Standard first, Reserve second AllocationSourceName =factor(AllocationSourceName, levels =c("Standard", "Reserve")))%>%arrange(AllocationSourceName)# Rest of World allocation summary (excluding Ukraine)rest_of_world_summary<-project_summary%>%filter(AllocationYear>=2014, !is.na(AllocationSourceName),PooledFundName!="Ukraine")%>%group_by(AllocationSourceName)%>%summarise( Projects =n(), Budget =sum(Budget, na.rm =TRUE), .groups ='drop')%>%mutate( Region ="Rest of World",# Force factor ordering: Standard first, Reserve second AllocationSourceName =factor(AllocationSourceName, levels =c("Standard", "Reserve")))%>%arrange(AllocationSourceName)# Combine both datasets and ensure orderingcomparison_data<-bind_rows(ukraine_allocation_summary, rest_of_world_summary)%>%arrange(Region, AllocationSourceName)# Recalculate percentages within each regioncomparison_data<-comparison_data%>%group_by(Region)%>%mutate( Projects_Pct =round((Projects/sum(Projects))*100, 1), Budget_Pct =round((Budget/sum(Budget))*100, 1))%>%ungroup()%>%arrange(Region, AllocationSourceName)# Budget proportions chart - Standard at bottom (dark teal), Reserve on top (light teal)ggplot(comparison_data, aes(x =Region, y =Budget_Pct, fill =AllocationSourceName))+geom_col(position =position_stack(reverse =FALSE), width =0.6)+geom_text(aes(label =paste0(AllocationSourceName, "\n", Budget_Pct, "%\n($", round(Budget/1e6, 0), "M)")), position =position_stack(vjust =0.5, reverse =FALSE), color ="white", fontface ="bold", size =3.8, lineheight =0.85)+scale_fill_manual(values =c("Standard"="#005f73", "Reserve"="#94d2bd"), name ="Allocation Type", breaks =c("Standard", "Reserve"))+scale_y_continuous(labels =function(x)paste0(x, "%"), expand =expansion(mult =c(0, 0.02)))+labs( title ="Budget Distribution by Allocation Type: Ukraine vs Rest of World (2014-2025)", subtitle ="Standard allocations (dark teal) at bottom, Reserve allocations (light teal) on top", x =NULL, y ="Percentage of Budget")+theme_minimal()+theme( plot.title =element_text(face ="bold", size =14), plot.subtitle =element_text(size =11, color ="#666666"), panel.grid.major.x =element_blank(), axis.text.x =element_text(size =12, face ="bold"), legend.position ="top", legend.text =element_text(size =11, face ="bold"))
Some allocation rounds that partners perceived as reserved are recorded in the CBPF database as standard allocations. For example, the 2024 Kharkiv area-based allocation was treated by some partners as effectively reserved, as some eligible organizations were reportedly advised not to submit proposals. In the source data, however, these rounds are classified and counted as standard.
Show code
# Calculate budget by allocation source and year for UKRAINE only (2019-2025)ukraine_allocation_by_year<-project_summary%>%filter(PooledFundName=="Ukraine", AllocationYear>=2019,!is.na(AllocationSourceName))%>%group_by(AllocationYear, AllocationSourceName)%>%summarise(Total_Budget =sum(Budget, na.rm =TRUE), .groups ='drop')%>%# Force factor ordering BEFORE calculating percentagesmutate( AllocationSourceName =factor(AllocationSourceName, levels =c("Standard", "Reserve")))%>%arrange(AllocationYear, AllocationSourceName)%>%group_by(AllocationYear)%>%mutate( Year_Total =sum(Total_Budget), Percentage =round((Total_Budget/Year_Total)*100, 1))%>%ungroup()%>%arrange(AllocationYear, AllocationSourceName)# Visualization - line chart showing percentage over time# Convert to millions for displayukraine_allocation_by_year<-ukraine_allocation_by_year%>%mutate(Budget_Millions =Total_Budget/1e6)ggplot(ukraine_allocation_by_year, aes(x =AllocationYear, y =Percentage, color =AllocationSourceName, group =AllocationSourceName))+geom_line(linewidth =1.5)+geom_point(size =4)+geom_text(aes(label =paste0(Percentage, "%")), vjust =-1.2, size =3.5, fontface ="bold", show.legend =FALSE)+scale_color_manual(values =c("Standard"="#005f73", "Reserve"="#94d2bd"), name ="Allocation Type", breaks =c("Standard", "Reserve"))+scale_y_continuous(labels =function(x)paste0(x, "%"), limits =c(0, 100), breaks =seq(0, 100, 20), expand =expansion(mult =c(0.05, 0.15)))+scale_x_continuous(breaks =seq(2019, 2025, 1))+labs( title ="Ukraine Allocation Type Distribution Over Time (2019-2025)", subtitle ="Percentage of budget allocated through Standard vs Reserve modalities by year", x ="Allocation Year", y ="Percentage of Total Budget")+theme_minimal()+theme( plot.title =element_text(face ="bold", size =14), plot.subtitle =element_text(size =11, color ="#666666"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.minor =element_blank(), axis.text.x =element_text(size =11))
Although the balance between standard and reserve allocations appears to be correcting in 2025, it is important to note that since the counter-offensives of 2022, there have not been major events — such as large-scale population movements or recaptured territories — that would justify the recurrent use of the reserve modality. Ukraine’s humanitarian needs can be considered sustainably high, but not necessarily volatile.
The global CBPF guidelines state that only exceptionally should the reserve allocation be used for non-emergency purposes: “the Reserve Allocation may also be used to support special initiatives which may not be rapid onset or unforeseen per se…” (CBPF Global Guidelines, art. 136).
4.2 Budget Distribution by Type of Organization
CBPF allocations reach beneficiaries through four types of implementing partners: International NGOs (INGOs), National NGOs (NNGOs), UN Agencies, and a residual “Others” category. The balance between these partner types reflects both operational realities on the ground and broader policy commitments — notably the Grand Bargain pledge to channel at least 25% of humanitarian funding to local and national actors.
Show code
org_colors<-c("National NGO"="#2a9d8f","International NGO"="#005f73","UN Agency"="#e9c46a","Others"="#adb5bd")# --- Left panel: proportional comparison Ukraine vs Rest of World ---org_type_ukraine<-project_summary%>%filter(AllocationYear>=2014, !is.na(OrganizationType),PooledFundName=="Ukraine")%>%group_by(OrganizationType)%>%summarise(Budget =sum(Budget, na.rm =TRUE), Projects =n(), .groups ="drop")%>%mutate(Region ="Ukraine")org_type_row<-project_summary%>%filter(AllocationYear>=2014, !is.na(OrganizationType),PooledFundName!="Ukraine")%>%group_by(OrganizationType)%>%summarise(Budget =sum(Budget, na.rm =TRUE), Projects =n(), .groups ="drop")%>%mutate(Region ="Rest of World")org_type_comparison<-bind_rows(org_type_ukraine, org_type_row)%>%group_by(Region)%>%mutate( Budget_Pct =round((Budget/sum(Budget))*100, 1), OrganizationType =factor(OrganizationType, levels =names(org_colors)))%>%ungroup()p_comparison<-ggplot(org_type_comparison,aes(x =Region, y =Budget_Pct, fill =OrganizationType))+geom_col(position =position_stack(reverse =TRUE), width =0.55)+geom_text(aes(label =paste0(OrganizationType, "\n", Budget_Pct, "%")), position =position_stack(vjust =0.5, reverse =TRUE), color ="white", fontface ="bold", size =3.2, lineheight =0.9)+scale_fill_manual(values =org_colors, name ="Organization Type")+scale_y_continuous(labels =function(x)paste0(x, "%"), expand =expansion(mult =c(0, 0.02)))+labs(title ="Ukraine vs Rest of World", subtitle ="Budget share by org type (2014-2025)", x =NULL, y ="Percentage of Budget")+theme_minimal()+theme( plot.title =element_text(face ="bold", size =13), plot.subtitle =element_text(size =10, color ="#666666"), panel.grid.major.x =element_blank(), axis.text.x =element_text(size =11, face ="bold"), legend.position ="none")# --- Right panel: Ukraine absolute budget over time ---ukraine_org_year<-project_summary%>%filter(PooledFundName=="Ukraine", AllocationYear>=2022,!is.na(OrganizationType))%>%group_by(AllocationYear, OrganizationType)%>%summarise(Budget =sum(Budget, na.rm =TRUE), .groups ="drop")%>%mutate(OrganizationType =factor(OrganizationType, levels =names(org_colors)))p_over_time<-ggplot(ukraine_org_year,aes(x =as.factor(AllocationYear), y =Budget/1e6, fill =OrganizationType))+geom_col(position =position_stack(reverse =TRUE), width =0.7, alpha =0.9)+scale_fill_manual(values =org_colors, name ="Organization Type")+scale_y_continuous(labels =scales::comma, expand =expansion(mult =c(0, 0.08)))+labs(title ="Ukraine Budget by Organization Type Over Time", subtitle ="USD millions (2022-2025)", x ="Allocation Year", y ="Budget (USD millions)")+theme_minimal()+theme( plot.title =element_text(face ="bold", size =13), plot.subtitle =element_text(size =10, color ="#666666"), axis.text.x =element_text(size =9, angle =45, hjust =1), legend.position ="top", legend.text =element_text(size =9))p_comparison+p_over_time+plot_layout(widths =c(1, 2))
4.3 Budget Distribution by Sector (Cluster)
This section examines how humanitarian funding is distributed across sectors, comparing the global CBPF portfolio with Ukraine’s UHF allocations (2014–2025). Sector labels in the CBPF system correspond to inter-agency clusters (e.g., Health, WASH, Protection).
4.3.1 Global vs Ukraine: Sector Budget Share
Show code
# Colour palette: one shade per sourcesource_colors<-c("Global CBPF"="#94d2bd", "Ukraine UHF"="#005f73")ggplot(comparison_df,aes(x =Cluster, y =Share, fill =Source))+geom_col(position =position_dodge(width =0.75), width =0.65, alpha =0.9)+geom_text(aes(label =paste0(round(Share, 1), "%")), position =position_dodge(width =0.75), hjust =-0.15, size =3.2, fontface ="bold")+coord_flip()+scale_fill_manual(values =source_colors, name =NULL)+scale_y_continuous( labels =function(x)paste0(x, "%"), expand =expansion(mult =c(0, 0.25)))+labs( title ="Sector Budget Share: Global CBPF vs Ukraine UHF (2014–2025)", subtitle ="Percentage of total allocated budget per sector", x =NULL, y ="Share of Total Budget (%)", caption ="Source: CBPF Cluster dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank(), axis.text.y =element_text(size =11))
Key observations: Emergency Shelter & NFI and Protection dominate Ukraine’s portfolio—reflecting the conflict-driven displacement crisis—while globally WASH and Health are the largest sectors. Ukraine’s WASH and Health shares are notably lower than the global average, highlighting a context-specific funding pattern.
4.3.2 Ukraine Sector Budget Evolution Over Time
The stacked view below tracks how the Ukraine UHF cluster mix has evolved year-on-year since the full-scale invasion. Health is highlighted in red — its absolute envelope has grown, but its share of the portfolio remains compressed by the scale of Protection and Shelter / NFI spending.
Show code
# Yearly breakdown for Ukraine per clusterukraine_yearly<-clusters_clean%>%filter(PooledFundName=="Ukraine", AllocationYear>=2022)%>%group_by(AllocationYear, Cluster)%>%summarise(Budget_M =sum(ClusterBudget, na.rm =TRUE)/1e6, .groups ="drop")sector_palette<-c("WASH"="#0077B6","Health"="#e63946","Food Security"="#90E0EF","Emergency Shelter & NFI"="#005f73","Protection"="#0A9396","Nutrition"="#94D2BD","Education"="#E9C46A","Camp Coordination/Mgmt"="#F4A261","Multi-purpose CASH"="#E76F51","Coord. & Support Services"="#ae2012","Multi-Sector"="#9b2226","Early Recovery"="#6d6875","Logistics"="#b5b5b5","Emergency Telecoms"="#d4d4d4","COVID-19"="#ccc5b9")ukraine_yearly<-ukraine_yearly%>%mutate(Cluster =factor(Cluster, levels =rev(cluster_order)))ggplot(ukraine_yearly,aes(x =as.factor(AllocationYear), y =Budget_M, fill =Cluster))+geom_col(position =position_stack(reverse =TRUE), width =0.75, alpha =0.92)+scale_fill_manual(values =sector_palette, name ="Sector")+scale_y_continuous( labels =scales::comma, expand =expansion(mult =c(0, 0.06)))+labs( title ="Ukraine UHF: Sector Budget Distribution Over Time (2022–2025)", subtitle ="USD millions, stacked by cluster | \u2665 Health sector highlighted", x ="Allocation Year", y ="Budget (USD millions)", caption ="Source: CBPF Cluster dataset, Feb 2026 extract")+guides(fill =guide_legend(nrow =3, byrow =TRUE))+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="bottom", legend.text =element_text(size =9), panel.grid.minor =element_blank(), axis.text.x =element_text(size =10, angle =45, hjust =1))
4.3.3 Divergence from Global Average
Show code
# Calculate difference: Ukraine share minus global sharedivergence_df<-ukraine_cluster%>%select(Cluster, Ukraine_Share =Share)%>%left_join(global_cluster%>%select(Cluster, Global_Share =Share), by ="Cluster")%>%mutate( Global_Share =replace_na(Global_Share, 0), Diff =Ukraine_Share-Global_Share, Direction =if_else(Diff>=0, "Above global average", "Below global average"), Cluster =factor(Cluster, levels =cluster_order))ggplot(divergence_df,aes(x =Cluster, y =Diff, fill =Direction))+geom_col(width =0.7, alpha =0.9)+geom_hline(yintercept =0, linewidth =0.8, color ="grey30")+geom_text(aes(label =paste0(if_else(Diff>0, "+", ""), round(Diff, 1), " pp"), hjust =if_else(Diff>=0, -0.1, 1.1)), size =3.4, fontface ="bold")+coord_flip()+scale_fill_manual( values =c("Above global average"="#005f73", "Below global average"="#e76f51"), name =NULL)+scale_y_continuous( labels =function(x)paste0(x, " pp"), expand =expansion(mult =c(0.25, 0.25)))+labs( title ="Ukraine UHF Sector Share vs Global CBPF Average (2014–2025)", subtitle ="Percentage-point difference (Ukraine minus global); pp = percentage points", x =NULL, y ="Difference (percentage points)", caption ="Source: CBPF Cluster dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank(), axis.text.y =element_text(size =11))
5 Grant Characteristics and Patterns
This chapter examines how large individual grants are across the CBPF system, comparing global patterns with Ukraine. Grant size reflects strategic choices: larger grants reduce transaction costs but concentrate risk; smaller grants enable broader partner diversity.
5.1 Grant Size Distribution
5.1.1 Grant Size: Global vs Ukraine
The density plot below uses a log₁₀ scale to handle the wide spread of grant values (from ~$25K to $29M). Both distributions are right-skewed; the key question is whether Ukraine’s curve sits systematically to the right.
Show code
# Summary stats for annotationmedians<-grants%>%group_by(Context)%>%summarise(med =median(Budget), .groups ="drop")fmt_usd<-function(x){ifelse(x>=1e6,paste0("$", formatC(x/1e6, format ="f", digits =2), "M"),paste0("$", scales::comma(round(x/1e3)), "K"))}ggplot(grants, aes(x =Budget, fill =Context, color =Context))+geom_density(alpha =0.35, linewidth =0.9, adjust =0.8)+geom_vline(data =medians,aes(xintercept =med, color =Context), linetype ="dashed", linewidth =1.1)+geom_label(data =medians,aes(x =med, y =Inf, label =paste0("Median\n", fmt_usd(med)), color =Context), vjust =1.3, hjust =-0.08, size =3.5, fontface ="bold", fill ="white", label.size =0.3, show.legend =FALSE)+scale_x_log10( labels =scales::label_dollar(scale_cut =scales::cut_short_scale()), breaks =c(1e4, 5e4, 1e5, 5e5, 1e6, 5e6, 1e7))+scale_fill_manual(values =ctx_colors, name =NULL)+scale_color_manual(values =ctx_colors, name =NULL)+labs( title ="Grant Size Distribution: Global CBPF vs Ukraine UHF (2014–2025)", subtitle ="Log₁₀ scale — dashed lines mark medians. Ukraine grants are substantially larger.", x ="Grant size (USD, log scale)", y ="Density", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.minor =element_blank())
Ukraine grants are 2.6× larger at the median — $1.13M vs $440K globally.
There is no available data on grant underspending. As a result, it is unclear how often partners do not fully meet their commitments while continuing to receive funding.
5.1.2 INGO vs NNGO Grant Size: A Deeper Look
Globally (excluding Ukraine), INGOs receive grants about 25 % larger than NNGOs at the median (ratio ≈ 1.25×). Ukraine’s ratio (1.40×) is mid-table but masks a striking post-2022 shift.
5.1.3 How Does Ukraine Compare Across All Funds?
Show code
# Per-fund medians for INGO and NNGO (min 10 grants each)fund_ngotype<-project_summary%>%filter(AllocationYear>=2014,!is.na(Budget), Budget>0,OrganizationType%in%c("International NGO", "National NGO"))%>%group_by(PooledFundName, OrganizationType)%>%summarise(med =median(Budget), n =n(), .groups ="drop")fund_wide<-fund_ngotype%>%pivot_wider(names_from =OrganizationType, values_from =c(med, n))%>%rename(ingo_med =`med_International NGO`, nngo_med =`med_National NGO`, ingo_n =`n_International NGO`, nngo_n =`n_National NGO`)%>%filter(!is.na(ingo_med), !is.na(nngo_med),ingo_n>=10, nngo_n>=10)%>%mutate( ratio =ingo_med/nngo_med, is_ukraine =PooledFundName=="Ukraine", PooledFundName =factor(PooledFundName, levels =PooledFundName[order(ratio)]))# Long form for dumbbelldumbbell_df<-fund_wide%>%select(PooledFundName, ingo_med, nngo_med, ratio, is_ukraine)%>%pivot_longer(cols =c(ingo_med, nngo_med), names_to ="OrgType", values_to ="Median")%>%mutate(OrgType =if_else(OrgType=="ingo_med", "INGO", "NNGO"))ggplot()+# segment connecting NNGO to INGO per fundgeom_segment( data =fund_wide,aes(x =nngo_med/1e3, xend =ingo_med/1e3, y =PooledFundName, yend =PooledFundName, color =is_ukraine), linewidth =1.2, alpha =0.7)+# dots for each org typegeom_point( data =dumbbell_df,aes(x =Median/1e3, y =PooledFundName, shape =OrgType, color =is_ukraine), size =3.5, stroke =1.0)+# ratio label to the rightgeom_text( data =fund_wide,aes(x =pmax(ingo_med, nngo_med)/1e3, y =PooledFundName, label =paste0(round(ratio, 2), "×"), fontface =if_else(is_ukraine, "bold", "plain")), hjust =-0.25, size =3.1, color ="grey30")+scale_x_continuous( labels =scales::dollar_format(suffix ="K", accuracy =1), expand =expansion(mult =c(0.02, 0.18)))+scale_color_manual( values =c("TRUE"="#f4a261", "FALSE"="#94d2bd"), guide ="none")+scale_shape_manual(values =c("INGO"=16, "NNGO"=1), name =NULL)+labs( title ="Median Grant Size: INGO vs NNGO per CBPF Fund (2014–2025)", subtitle ="Sorted by INGO/NNGO ratio (right labels) | <b style='color:#f4a261'>Ukraine highlighted</b> | filled = INGO, open = NNGO", x ="Median grant size (USD thousands)", y =NULL, caption ="Funds with < 10 grants per org type excluded")+theme_minimal(base_size =12)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_markdown(size =10, color ="#555555"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank(), legend.position ="top", legend.text =element_text(size =11, face ="bold"), axis.text.y =element_text( size =ifelse(levels(fund_wide$PooledFundName)=="Ukraine", 12, 10), face =ifelse(levels(fund_wide$PooledFundName)=="Ukraine", "bold", "plain"), color =ifelse(levels(fund_wide$PooledFundName)=="Ukraine", "#f4a261", "grey30")))
What the chart presents: Each horizontal segment represents one CBPF fund. The open circle is the NNGO median grant, the filled circle is the INGO median. The further right, the larger the absolute grants. The ratio label is what matters for the comparison.
The headline finding: Ukraine’s ratio is unremarkable At 1.40×, Ukraine sits mid-pack — 14th of 24 funds that have at least 10 INGO and 10 NNGO grants in 2014–2025. The figure is close to the non-Ukraine global benchmark (≈1.25×) and far from the high-ratio contexts (Sudan 2.26×, Nigeria 2.00×). This is counterintuitive given Ukraine’s reputation as a progressive localization context. The ratio alone doesn’t signal any particular INGO advantage.
But the absolute values tell a different story Ukraine’s NNGO median (~$999K) is likely higher in absolute terms than many funds’ INGO medians. A Ukrainian NNGO receives grants that would be considered large even by INGO standards in Sudan or Nigeria. The localization story in Ukraine is not just about equality of ratio — it’s that a smaller group of persisting selections operates at a much higher scale.
The implication for Ukraine: The ratio of 1.40× aggregated over 2014–2025 understates how much has changed recently — that’s exactly what the trend chart below it reveals, where the ratio collapsed to 0.84× in 2024. The aggregate figure is dragged up by the early pre-war years when Ukraine operated more like a typical fund.
5.1.4 Ukraine: INGO/NNGO Grant Ratio Over Time
Since 2022, Ukraine’s NNGO grants have grown faster than INGO grants. In 2023 and 2024 the ratio fell below 1 — NNGOs received larger median grants than INGOs — a direct signal of the localization push intensifying after the full-scale invasion.
Show code
# Year-by-year ratio for Ukraine and global averagengo_ratio_all<-project_summary%>%filter(AllocationYear>=2014,!is.na(Budget), Budget>0,OrganizationType%in%c("International NGO", "National NGO"))%>%group_by(PooledFundName, AllocationYear, OrganizationType)%>%summarise(med =median(Budget), n =n(), .groups ="drop")make_ratio<-function(df){df%>%pivot_wider(names_from =OrganizationType, values_from =c(med, n))%>%rename(ingo =`med_International NGO`, nngo =`med_National NGO`, n_i =`n_International NGO`, n_n =`n_National NGO`)%>%filter(!is.na(ingo), !is.na(nngo), n_i>=5, n_n>=5)%>%mutate(ratio =ingo/nngo)}ukraine_ratio<-ngo_ratio_all%>%filter(PooledFundName=="Ukraine")%>%make_ratio()%>%mutate(Series ="Ukraine UHF")global_ratio<-ngo_ratio_all%>%filter(PooledFundName!="Ukraine")%>%group_by(AllocationYear, OrganizationType)%>%summarise(med =median(med), n =sum(n), .groups ="drop")%>%mutate(PooledFundName ="Global")%>%make_ratio()%>%mutate(Series ="Global CBPF average")ratio_trend<-bind_rows(ukraine_ratio, global_ratio)series_colors<-c("Ukraine UHF"="#005f73", "Global CBPF average"="#94d2bd")ggplot(ratio_trend,aes(x =AllocationYear, y =ratio, color =Series, group =Series))+geom_hline(yintercept =1, linetype ="dashed", color ="#e63946", linewidth =0.9)+annotate("text", x =2014.2, y =1.03, label ="Parity (INGO = NNGO)", color ="#e63946", size =3.4, hjust =0, fontface ="italic")+geom_ribbon( data =ratio_trend%>%filter(Series=="Ukraine UHF"),aes(ymin =1, ymax =ratio, fill =ratio<1), alpha =0.15, color =NA)+geom_line(linewidth =1.5)+geom_point(size =4)+geom_text(aes(label =round(ratio, 2)), vjust =-1.1, size =3.3, fontface ="bold", show.legend =FALSE)+scale_color_manual(values =series_colors, name =NULL)+scale_fill_manual(values =c("FALSE"="#005f73", "TRUE"="#e63946"), guide ="none")+scale_x_continuous(breaks =2014:2025)+scale_y_continuous( labels =function(x)paste0(x, "×"), breaks =seq(0.7, 1.8, 0.1))+labs( title ="INGO/NNGO Median Grant Ratio Over Time: Ukraine vs Global (2014–2025)", subtitle ="Ratio > 1 means INGOs get larger grants | Red shading = years where NNGOs received larger grants (ratio < 1)", x ="Allocation Year", y ="INGO median / NNGO median", caption ="Global = median of fund-level medians, excluding Ukraine")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.minor =element_blank(), axis.text.x =element_text(angle =45, hjust =1, size =10))
Ukraine’s localization in action: while globally the INGO size premium has been stable at ~1.2–1.4×, Ukraine’s ratio dropped to 0.94× in 2023 and 0.84× in 2024, meaning NNGOs were receiving larger individual grants than INGOs. This likely reflects deliberate fund strategy to direct larger, more complex grants to national partners after the scale-up in 2022.
5.1.5 Grant Size by Sector — Health in Context
Using each project’s primary cluster (the sector receiving the highest budget share), this chart compares median grant sizes across sectors. Health grants are highlighted in red.
Show code
# Compute median + IQR per cluster for both contexts combinedsector_stats<-grants_cl%>%filter(Cluster!="Unknown")%>%group_by(Cluster)%>%summarise( med =median(Budget), q1 =quantile(Budget, 0.25), q3 =quantile(Budget, 0.75), n =n(), .groups ="drop")%>%arrange(med)%>%mutate( Cluster =factor(Cluster, levels =Cluster), is_health =Cluster=="Health")# Separate Ukraine medians to overlayukraine_med<-grants_cl%>%filter(Cluster!="Unknown", PooledFundName=="Ukraine")%>%group_by(Cluster)%>%summarise(ukraine_med =median(Budget), .groups ="drop")%>%mutate(Cluster =factor(Cluster, levels =levels(sector_stats$Cluster)))sector_plot_df<-sector_stats%>%left_join(ukraine_med, by ="Cluster")ggplot(sector_plot_df,aes(x =Cluster, y =med/1e3, fill =is_health))+geom_col(width =0.7, alpha =0.88)+gghighlight(is_health, unhighlighted_params =list(fill ="#94d2bd", alpha =0.6), use_direct_label =FALSE)+# Ukraine medians as pointsgeom_point(aes(y =ukraine_med/1e3), shape =23, size =3.5, fill ="#005f73", color ="white", stroke =0.8, na.rm =TRUE)+geom_text(aes(label =paste0("$", scales::comma(round(med/1e3)), "K")), hjust =-0.1, size =3.2, fontface ="bold")+coord_flip()+scale_fill_manual(values =c("FALSE"="#94d2bd", "TRUE"="#e63946"), guide ="none")+scale_y_continuous( labels =scales::dollar_format(suffix ="K", accuracy =1), expand =expansion(mult =c(0, 0.3)))+labs( title ="Median Grant Size by Sector — Global CBPF (2014–2025)", subtitle ="<b style='color:#e63946'>♥ Health</b> highlighted | <b style='color:#005f73'>◆ Diamond</b> = Ukraine UHF median", x =NULL, y ="Median grant size (USD thousands)", caption ="Source: CBPF ProjectSummary + Cluster datasets, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_markdown(size =11, color ="#555555"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank(), axis.text.y =element_markdown( size =ifelse(levels(sector_plot_df$Cluster)=="Health", 12, 11), face =ifelse(levels(sector_plot_df$Cluster)=="Health", "bold", "plain"), color =ifelse(levels(sector_plot_df$Cluster)=="Health", "#e63946", "grey30")))
Health grants rank in the middle of the pack globally (~$422K median), well below sectors like Logistics, Multi-purpose Cash, and Shelter & NFI. The ◆ diamonds show that Ukraine’s health grants are significantly larger than the global norm, consistent with the fund’s overall pattern of larger, more consolidated grants.
5.1.6 Grant Size vs Number of Grants per Organisation
Each dot is one implementing organisation. The x-axis shows how many grants that organisation received over 2022–2025; the y-axis shows their median grant size. Organisations in the top-left corner are high-value, low-frequency recipients; those in the bottom-right are high-frequency implementers of smaller grants.
Interactive Dashboard: Explore this visualization interactively at shiny.baena.info/uhf-grant-size-2025 where you can filter by organization type, explore individual organizations, and hover for details.
Show code
# Prepare dataorg_stats<-grants_cl%>%filter(AllocationYear>=2022, AllocationYear<=2025,!is.na(Budget), Budget>0,!is.na(OrganizationName))%>%group_by(Context, OrganizationName, OrganizationType)%>%summarise( n_grants =n(), median_size =median(Budget), total_usd =sum(Budget), pct_health =mean(Cluster=="Health"), .groups ="drop")%>%mutate( dot_group =case_when(pct_health>=0.5~"Health (≥50% health grants)",Context=="Ukraine UHF"~"Ukraine UHF",TRUE~"Global CBPF"))# Dynamic ceilings: y = max Ukraine median + 4M, x = max Ukraine n_grants + 3ukr_max<-org_stats%>%filter(Context=="Ukraine UHF")%>%pull(median_size)%>%max()ukr_xmax<-org_stats%>%filter(Context=="Ukraine UHF")%>%pull(n_grants)%>%max()y_ceiling<-ukr_max+4e6x_ceiling<-ukr_xmax+3dot_colors<-c("Ukraine UHF"="#f4a261","Global CBPF"="#94d2bd","Health (≥50% health grants)"="#e63946")# Parametric ellipse for annotation (log-y space)theta<-seq(0, 2*pi, length.out =200)ellipse_df<-data.frame( x =9+4.2*cos(theta), y =10^(6.45+0.55*sin(theta)))ggplot(org_stats,aes(x =n_grants, y =median_size, color =dot_group, size =total_usd))+geom_path(data =ellipse_df, aes(x =x, y =y), inherit.aes =FALSE, color ="black", linewidth =0.9, linetype ="solid")+annotate("text", x =9, y =10^6.9, label ="The UHF Exception", hjust =0.5, vjust =1, size =4.5, fontface ="bold.italic", color ="black")+geom_point(alpha =0.65, stroke =0)+scale_y_log10( labels =scales::label_dollar(scale_cut =scales::cut_short_scale()), breaks =c(1e4, 5e4, 1e5, 5e5, 1e6, 5e6, 1e7))+scale_x_continuous(labels =scales::label_comma(), breaks =c(1, 2, 3, 5, 7, 10, 15, 20))+scale_color_manual(values =dot_colors, name =NULL, guide =guide_legend(override.aes =list(size =4)))+scale_size_continuous( name ="Total disbursed (2022–2025)", range =c(2, 16), labels =scales::label_dollar(scale_cut =scales::cut_short_scale()), breaks =c(1e6, 5e6, 1e7, 3e7, 5e7), trans ="sqrt")+coord_cartesian(xlim =c(NA, x_ceiling), ylim =c(NA, y_ceiling))+labs( title ="Grant Size vs Number of Grants per Organisation (2022–2025)", subtitle ="Each dot = one organisation · Y-axis log-scaled · Dot size = total disbursed · Red = majority health grants", x ="Number of grants received", y ="Median grant size (USD, log scale)", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract. Axes clipped at max Ukraine values + buffer.")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="bottom", panel.grid.minor =element_blank())
International Charitable Organization “East Europe Foundation”
National NGO
2
$1.60M
$3.21M
0%
FONDAZIONE TERRE DES HOMMES ITALIA - ONLUS
International NGO
3
$1.01M
$3.20M
0%
Deutsche Welthungerhilfe e.V.
International NGO
3
$877.56K
$2.97M
0%
NON-GOVERNMENTAL ORGANIZATION “GIRLS”
National NGO
3
$983.74K
$2.93M
0%
Medair
International NGO
2
$1.38M
$2.75M
0%
The International Charitable Foundation “Alliance for Public Health”
National NGO
2
$1.35M
$2.70M
100%
Charitable Organization ‘International Charitable Foundation ’Friends’ Hands’
National NGO
3
$967.47K
$2.69M
0%
Adventist Development and Relief Agency – Ukraine
National NGO
1
$2.69M
$2.69M
0%
United Nations Population Fund
UN Agency
2
$1.31M
$2.61M
0%
Volontariato Internazionale per lo Sviluppo
International NGO
2
$1.25M
$2.50M
0%
Cesvi Fondazione - ETS
International NGO
1
$2.50M
$2.50M
0%
Fondazione We World - GVC Onlus
International NGO
3
$1.00M
$2.50M
0%
Ukrainian Deminers Association
National NGO
4
$594.00K
$2.48M
0%
Folkekirkens Nødhjælp
International NGO
2
$1.20M
$2.40M
0%
CARE Deutschland e. V.
International NGO
1
$2.39M
$2.39M
0%
Non-Governmental Organization Resource Center
National NGO
4
$661.46K
$2.36M
0%
HIAS Inc.
International NGO
1
$2.20M
$2.20M
0%
Solidarités International
International NGO
2
$1.10M
$2.20M
0%
NEW DAWN
National NGO
3
$843.74K
$2.06M
0%
CHARITABLE ORGANIZATION ‘CHARITY FUND ’GOODWILL’
National NGO
2
$1.00M
$2.00M
0%
Medicos del Mundo
International NGO
2
$950.00K
$1.90M
100%
arche noVa (Representative office of arche noVa in Ukraine)
International NGO
1
$1.88M
$1.88M
0%
Foundazione di Religione “Opera San Francesco Saverio” - Collegio Universitario Aspiranti Medici Missionari - C.U.A.M.M.
International NGO
3
$495.50K
$1.72M
100%
humedica e.V.
International NGO
2
$762.85K
$1.53M
100%
Federation Handicap International
International NGO
1
$1.50M
$1.50M
0%
ChildFund Deutschland e. V.
International NGO
2
$711.70K
$1.42M
0%
MTU Mondo
International NGO
2
$585.16K
$1.17M
0%
Stichting War Child Alliance
International NGO
1
$1.12M
$1.12M
0%
Internationale Gesellschaft für Menschenrechte
International NGO
2
$526.65K
$1.05M
0%
CHARITY ORGANISATION “CHARITY FUND “ALPS RESILIENCE UKRAINE”
International NGO
1
$999.89K
$999.89K
0%
The Alliance for International Medical Action
International NGO
1
$700.00K
$700.00K
100%
Cooperative Housing Foundation
International NGO
1
$699.41K
$699.41K
0%
Action Contre la Faim
International NGO
1
$697.06K
$697.06K
0%
Charitable Organization “Light Of Hope”
National NGO
1
$693.12K
$693.12K
100%
Spring of Hope. Ukraine
National NGO
1
$644.73K
$644.73K
0%
International Children’s Fund ‘Mira’
National NGO
2
$321.59K
$643.18K
0%
Bureau of gender strategy and budgeting
National NGO
1
$615.78K
$615.78K
0%
TSE - NASHA SPRAVA!
National NGO
2
$249.94K
$499.88K
0%
NGO Divergent Woman
National NGO
2
$244.00K
$487.99K
0%
Podolian Agency for Regional Development
National NGO
1
$399.68K
$399.68K
0%
Integration Center
National NGO
1
$249.99K
$249.99K
0%
Charitable Organization “Charitable Foundation ‘For the Future of Ukraine’
National NGO
1
$249.98K
$249.98K
0%
Public Organization Common Cause for People
National NGO
1
$249.98K
$249.98K
0%
LAMPA
National NGO
1
$249.97K
$249.97K
0%
Public Organization YES
National NGO
1
$249.58K
$249.58K
0%
Charitable Organization “Charitable Foundation “Mission Kharkiv”
National NGO
1
$248.35K
$248.35K
100%
NGO “Airlight”
National NGO
1
$238.19K
$238.19K
100%
Charity Fund “VOLUNTEER MOVEMENT OF BUKOVYNA”
National NGO
1
$237.55K
$237.55K
0%
6 Partners per Allocation
Each CBPF allocation is a discrete funding round (e.g. “1st Standard Allocation 2023 — Ukraine”). The number of implementing partners selected per round is a direct signal of concentration: a fund that channels money through 10 partners per allocation is far more concentrated than one that routes it through 40. Ukraine’s profile as a high-intensity conflict fund, combined with its relatively small NNGO ecosystem, raises the question of whether it selects fewer partners than comparable funds globally.
6.1 Distribution: Ukraine vs Global
Show code
# Number of distinct partners per allocation round# Use AllocationTypeId (e.g. "2022 3rd Reserve Allocation") as the round-level key.# AllocationSourceID only takes 2 values (Standard/Reserve) and would conflate rounds.partners_per_alloc<-project_summary%>%filter(AllocationYear>=2014,!is.na(AllocationTypeId),!is.na(OrganizationName))%>%group_by(PooledFundName, AllocationTypeId, AllocationType,AllocationSourceName, AllocationYear)%>%summarise( n_partners =n_distinct(OrganizationName), total_budget =sum(Budget, na.rm =TRUE), n_projects =n(), .groups ="drop")%>%mutate(Context =if_else(PooledFundName=="Ukraine", "Ukraine UHF", "Global CBPF"))# Summary stats for calloutppa_summary<-partners_per_alloc%>%group_by(Context)%>%summarise( n_allocs =n(), med =median(n_partners), q1 =quantile(n_partners, 0.25), q3 =quantile(n_partners, 0.75), mean_n =round(mean(n_partners), 1), .groups ="drop")
Show code
ctx_colors_ppa<-c("Ukraine UHF"="#005f73", "Global CBPF"="#94d2bd")med_labels<-partners_per_alloc%>%group_by(Context)%>%summarise(med =median(n_partners), .groups ="drop")ggplot(partners_per_alloc,aes(x =n_partners, fill =Context, color =Context))+geom_density(alpha =0.35, linewidth =0.9, adjust =0.9)+geom_vline(data =med_labels,aes(xintercept =med, color =Context), linetype ="dashed", linewidth =1.1)+geom_label(data =med_labels,aes(x =med, y =Inf, label =paste0("Median: ", round(med), " partners"), color =Context), vjust =1.3, hjust =-0.07, size =3.5, fontface ="bold", fill ="white", label.size =0.3, show.legend =FALSE)+scale_fill_manual(values =ctx_colors_ppa, name =NULL)+scale_color_manual(values =ctx_colors_ppa, name =NULL)+scale_x_continuous(breaks =c(5, 10, 20, 30, 40, 50, 60, 80, 100))+labs( title ="Partners per Allocation Round: Global CBPF vs Ukraine UHF (2014–2025)", subtitle ="Each observation is one allocation round; dashed lines mark medians", x ="Number of distinct implementing partners", y ="Density", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11, face ="bold"), panel.grid.minor =element_blank())
Ukraine allocations reach 11.5 partners at the median vs 7 globally — Ukraine rounds involve roughly 1.6× more partners per allocation. Combined with the larger median grant size, this points not to a narrower partner base but to a bigger operational footprint per round — a reflection of the scale of needs and of the UHF’s ability to mobilise a wide partner pool.
Method note: each observation is one discrete allocation round identified by AllocationTypeId (e.g. “2022 3rd Reserve Allocation”, “2024 1st Standard Allocation”).
6.2 Partners per Allocation: Trend Over Time (Ukraine)
Ukraine’s partner count per allocation has evolved markedly since the full-scale invasion in 2022.
Show code
# Yearly median and IQR for Ukraineukr_trend<-partners_per_alloc%>%filter(Context=="Ukraine UHF")%>%group_by(AllocationYear)%>%summarise( med =median(n_partners), q1 =quantile(n_partners, 0.25), q3 =quantile(n_partners, 0.75), n_obs =n(), .groups ="drop")# Global yearly median for comparisonglob_trend<-partners_per_alloc%>%filter(Context=="Global CBPF")%>%group_by(AllocationYear)%>%summarise( med =median(n_partners), q1 =quantile(n_partners, 0.25), q3 =quantile(n_partners, 0.75), .groups ="drop")ggplot()+# Global ribbon + linegeom_ribbon(data =glob_trend,aes(x =AllocationYear, ymin =q1, ymax =q3), fill ="#94d2bd", alpha =0.25)+geom_line(data =glob_trend,aes(x =AllocationYear, y =med, color ="Global CBPF median"), linewidth =0.9, linetype ="dotted")+# Ukraine ribbon + linegeom_ribbon(data =ukr_trend,aes(x =AllocationYear, ymin =q1, ymax =q3), fill ="#005f73", alpha =0.20)+geom_line(data =ukr_trend,aes(x =AllocationYear, y =med, color ="Ukraine UHF median"), linewidth =1.2)+geom_point(data =ukr_trend,aes(x =AllocationYear, y =med, color ="Ukraine UHF median"), size =3)+geom_text(data =ukr_trend,aes(x =AllocationYear, y =med, label =round(med)), vjust =-0.9, size =3.2, fontface ="bold", color ="#005f73")+annotate("rect", xmin =2021.5, xmax =Inf, ymin =-Inf, ymax =Inf, fill ="#e63946", alpha =0.04)+annotate("text", x =2022, y =Inf, label ="Full-scale\ninvasion", vjust =1.4, hjust =0, size =3, color ="#e63946", fontface ="italic")+scale_color_manual( values =c("Ukraine UHF median"="#005f73", "Global CBPF median"="#2ca58d"), name =NULL)+scale_x_continuous(breaks =2014:2025)+labs( title ="Median Partners per Allocation Round by Year", subtitle ="Shaded bands = IQR; dotted line = global median; solid = Ukraine", x ="Allocation year", y ="Partners per allocation (median)", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, color ="#005f73"), plot.subtitle =element_text(size =11, color ="#555555"), legend.position ="top", legend.text =element_text(size =11), panel.grid.minor =element_blank(), axis.text.x =element_text(angle =45, hjust =1))
7 Concentration, Scale, and the Core-20
If UHF involves more partners per round than peer funds and grants larger amounts per grant, a natural question follows: is the resulting portfolio more concentrated than peer CBPFs, and who sits at the top? This chapter answers both questions quantitatively. The headline is counterintuitive: UHF is not more concentrated than comparable CBPFs — it is simply larger. The appearance of concentration is produced by the interaction of envelope size with the $5M per-grant cap, and by a stable 20-organisation core that has recurred in every allocation round since 2022.
7.1 Ukraine vs Peer CBPFs: Concentration Benchmark
Peer set: the seven other largest CBPFs by 2022–2024 envelope (Afghanistan, Syria Cross-border, Sudan, Yemen, Ethiopia, Somalia, oPt). Two natural classes emerge — mega-concentrated access funds (Sudan, Syria XB, with only 3–4 partners per year and HHI > 0.3) and broad partner funds (Afghanistan, Yemen, Ethiopia, Somalia, oPt, Ukraine). Ukraine sits in the second class.
Show code
peer_funds<-c("Ukraine", "Afghanistan", "Syria Cross border","Sudan", "Yemen", "Ethiopia", "Somalia", "oPt")conc_agg<-project_summary%>%filter(AllocationYear%in%2022:2024,PooledFundName%in%peer_funds,!is.na(Budget), Budget>0,!is.na(OrganizationName))%>%group_by(PooledFundName, OrganizationName)%>%summarise(amt =sum(Budget), .groups ="drop")%>%group_by(PooledFundName)%>%arrange(desc(amt), .by_group =TRUE)%>%mutate(share =amt/sum(amt))%>%summarise( n_partners =n(), envelope =sum(amt), HHI =sum(share^2), top5 =sum(share[1:min(5, n())]), top10 =sum(share[1:min(10, n())]), .groups ="drop")%>%mutate( Class =if_else(n_partners<=5,"Access-restricted (UN-dominant)","Broad partner fund"), is_ukraine =PooledFundName=="Ukraine")# Two aligned horizontal-bar panels, funds sorted by HHI.# Ukraine highlighted in red. The visual punchline: mid-row on HHI,# near-top row on envelope -> "same shape, bigger envelope."conc_plot<-conc_agg%>%mutate(Fund =forcats::fct_reorder(PooledFundName, HHI))bar_fill<-c(`TRUE` ="#c9184a", `FALSE` ="#8aa9b0")p_hhi<-ggplot(conc_plot, aes(x =HHI, y =Fund, fill =is_ukraine))+geom_col(width =0.7)+geom_text(aes(label =sprintf("%.3f", HHI)), hjust =-0.15, size =3.5, colour ="#333333")+scale_fill_manual(values =bar_fill, guide ="none")+scale_x_continuous(expand =expansion(mult =c(0, 0.18)))+labs(title ="Concentration (HHI)", subtitle ="Lower = more evenly spread across partners", x =NULL, y =NULL)+theme_minimal(base_size =12)+theme(plot.title =element_text(face ="bold", colour ="#005f73"), plot.subtitle =element_text(size =10, colour ="#555555"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank())p_env<-ggplot(conc_plot, aes(x =envelope/1e6, y =Fund, fill =is_ukraine))+geom_col(width =0.7)+geom_text(aes(label =scales::dollar(envelope/1e6, suffix ="M", accuracy =1)), hjust =-0.15, size =3.5, colour ="#333333")+scale_fill_manual(values =bar_fill, guide ="none")+scale_x_continuous(expand =expansion(mult =c(0, 0.22)))+labs(title ="Envelope 2022–2024", subtitle ="Absolute USD disbursed", x =NULL, y =NULL)+theme_minimal(base_size =12)+theme(plot.title =element_text(face ="bold", colour ="#005f73"), plot.subtitle =element_text(size =10, colour ="#555555"), axis.text.y =element_blank(), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank())(p_hhi|p_env)+plot_annotation( title ="Ukraine sits mid-pack on concentration — but tops the envelope ranking", subtitle ="Same fund order in both panels (sorted by HHI). Ukraine highlighted in red.", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract", theme =theme(plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555")))
CBPF concentration metrics, 2022–2024 (aggregated across the three years).
Fund
Envelope
Partners
HHI
Top-5 share
Top-10 share
Ukraine
$540.7M
94
0.029
28.3%
46.0%
Afghanistan
$472.8M
107
0.052
42.3%
52.8%
Syria Cross border
$350.2M
4
0.378
100.0%
100.0%
Sudan
$327.5M
4
0.818
100.0%
100.0%
Yemen
$201.6M
73
0.026
23.6%
38.3%
Ethiopia
$187.8M
65
0.037
32.1%
51.2%
Somalia
$161.0M
113
0.014
14.3%
24.8%
oPt
$158.5M
54
0.039
31.5%
52.5%
Reading the map. Among broad partner funds, Ukraine is unremarkable on shape — lower HHI than Afghanistan or Ethiopia, higher than Somalia or Yemen, mid-pack on top-5 (28%) and top-10 (46%) share. What makes it look concentrated is the envelope size: at $541M over three years UHF is twice Yemen’s volume on the same HHI. Moderate concentration × large envelope = large absolute disbursements to the top partners.
Show code
conc_year<-project_summary%>%filter(PooledFundName=="Ukraine",AllocationYear%in%2022:2025,!is.na(Budget), Budget>0,!is.na(OrganizationName))%>%group_by(AllocationYear, OrganizationName)%>%summarise(amt =sum(Budget), .groups ="drop")%>%group_by(AllocationYear)%>%arrange(desc(amt), .by_group =TRUE)%>%mutate(share =amt/sum(amt))%>%summarise( HHI =sum(share^2), top5 =sum(share[1:min(5, n())]), top10 =sum(share[1:min(10, n())]), .groups ="drop")%>%tidyr::pivot_longer(cols =c(top5, top10), names_to ="metric", values_to ="value")%>%mutate(metric =recode(metric, top5 ="Top-5 share", top10 ="Top-10 share"))ggplot(conc_year,aes(x =AllocationYear, y =value, colour =metric, group =metric))+geom_line(linewidth =1.3)+geom_point(size =3.5)+geom_text(aes(label =scales::percent(value, accuracy =1)), vjust =-1.1, size =3.2, fontface ="bold", show.legend =FALSE)+scale_colour_manual(values =c("Top-5 share"="#005f73","Top-10 share"="#94d2bd"), name =NULL)+scale_y_continuous(labels =scales::percent_format(), limits =c(0, 0.75), expand =expansion(mult =c(0, 0.05)))+scale_x_continuous(breaks =2022:2025)+labs( title ="UHF is de-concentrating: top-5 and top-10 partner shares over time", subtitle ="Ukraine UHF, 2022–2025 — partner shares calculated per year", x ="Allocation year", y ="Share of annual envelope", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555"), legend.position ="top", panel.grid.minor =element_blank())
The trajectory is toward more partners and smaller top-tier shares, not fewer. Top-5 share fell from 40% in 2022 to 28% in 2024; top-10 from 62% to 45%. Consistent with the localisation push documented in the 2024 Annual Report (NNGO share rising from 30% in 2023 to ~60% in 2024) and with the UN Agency exit (see below).
7.2 Why UHF Grants Are Bigger: An Arithmetic Identity
Because Ukraine’s partner count is similar to Afghanistan or Yemen but its envelope is roughly 2× larger, the average partner take mechanically has to be larger. This is not a policy choice — it’s arithmetic — and it explains most of the “larger grants” signal in Chapter 5.
Show code
peer_grants<-project_summary%>%filter(AllocationYear%in%2022:2024,PooledFundName%in%peer_funds,!is.na(Budget), Budget>0)ref_lines<-data.frame( yintercept =c(5e6, 8e6), label =c("Standard cap $5M", "Exceptional cap $8M"), colour =c("#e63946", "#e76f51"))ggplot(peer_grants%>%filter(PooledFundName%in%c("Ukraine","Afghanistan","Yemen","Ethiopia","oPt","Somalia")),aes(x =reorder(PooledFundName, Budget, FUN =median), y =Budget))+geom_boxplot(aes(fill =PooledFundName=="Ukraine"), outlier.size =0.9, outlier.alpha =0.5, width =0.55)+geom_hline(yintercept =5e6, linetype ="dashed", colour ="#e63946", linewidth =0.7)+annotate("text", x =0.6, y =5.3e6, label ="Standard cap $5M", colour ="#e63946", size =3.2, hjust =0, fontface ="italic")+scale_y_log10(labels =scales::label_dollar(scale_cut =scales::cut_short_scale()), breaks =c(5e4, 1e5, 5e5, 1e6, 2e6, 5e6, 1e7))+scale_fill_manual(values =c(`TRUE` ="#005f73", `FALSE` ="#94d2bd"), guide ="none")+coord_flip()+labs( title ="Grant-size distribution: Ukraine vs broad peer CBPFs (2022–2024)", subtitle ="Log-scale. Ukraine's 90th percentile sits exactly at the $5M standard cap — a ceiling-bound distribution no peer fund exhibits.", x =NULL, y ="Grant budget (USD, log scale)", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555"), panel.grid.major.y =element_blank(), panel.grid.minor =element_blank())
Show code
per_partner<-conc_agg%>%filter(Class=="Broad partner fund")%>%mutate( mean_per_partner =envelope/n_partners, Envelope =scales::dollar(envelope/1e6, suffix ="M", accuracy =0.1), `Mean \\$ per partner (3y)` =scales::dollar(mean_per_partner/1e6, suffix ="M", accuracy =0.01))%>%arrange(desc(mean_per_partner))%>%select(Fund =PooledFundName, Envelope, Partners =n_partners,`Mean \\$ per partner (3y)`)knitr::kable(per_partner, caption ="Mean 3-year envelope per partner in broad-partner CBPFs (2022–2024). UHF's arithmetic average is roughly 2× the next-largest fund.")
Mean 3-year envelope per partner in broad-partner CBPFs (2022–2024). UHF’s arithmetic average is roughly 2× the next-largest fund.
Fund
Envelope
Partners
Mean $ per partner (3y)
Ukraine
$540.7M
94
$5.75M
Afghanistan
$472.8M
107
$4.42M
oPt
$158.5M
54
$2.94M
Ethiopia
$187.8M
65
$2.89M
Yemen
$201.6M
73
$2.76M
Somalia
$161.0M
113
$1.42M
The P90 signature is unique to UHF. Ukraine is the only CBPF where the 90th percentile grant size sits exactly at the $5M standard cap — producing a flat-topped distribution. Peer funds show long right tails with a few mega-grants pulling the top; Ukraine shows a ceiling. The operational reading is that the UHF routinely writes max-size grants to a large number of partners simultaneously.
7.3 The Core-20: Who Captures the Majority
Filtering to organisations that received grants in every UHF allocation round 2022–2024 yields a core of 20 partners. Together they absorbed 55.3% of the $541M three-year envelope.
7.4 The Four Types, Operationalised
The article’s framing points to a key insight: partners cluster into four risk-bearing profiles based on their institutional scale relative to UHF engagement, and their operational history relative to the 2022 invasion. To move beyond qualitative description, each type is operationalised with a rule-based classification so that every partner (not just the Core-20) can be scored.
Partner typology: rule-based classification covering all 94 Ukraine partners
Type
Operational Rule
Risk Signature
D — Hyperscalers
UN Agency, or pre-war global revenue ≥ $50M
UHF volume is absorbable; institutional scale ≫ grant scale
Pre-2022 founding (revenue < $20M), or pre-2022 INGO with first CBPF grant ≥ 2022
UHF scale ≈ pre-war annual revenue; outside CBPF history exists
A — Born of the war
Founded 2022 or later; entity co-emergent with UHF
No independent baseline; UHF engagement is institutional track record
For the Core-20, classification is hardcoded using the revenue data in the rev_usd tibble and confirmed founding dates. For the remaining 74 partners in the 94-partner universe, the rules are applied automatically: Type A captures only organizations with confirmed founding year ≥ 2022; others classify via OrganizationType and founding/first-CBPF-year.
Show code
ukr22_24<-project_summary%>%filter(PooledFundName=="Ukraine",AllocationYear%in%2022:2024,!is.na(Budget), Budget>0,!is.na(OrganizationName))ukr25_partner<-project_summary%>%filter(PooledFundName=="Ukraine", AllocationYear==2025,!is.na(Budget), Budget>0, !is.na(OrganizationName))%>%group_by(OrganizationName)%>%summarise(n25 =n(), amt25 =sum(Budget, na.rm =TRUE), .groups ="drop")core20<-ukr22_24%>%group_by(OrganizationName, OrganizationType)%>%summarise( n_grants =n(), n_years =n_distinct(AllocationYear), total =sum(Budget, na.rm =TRUE), mean_grant =mean(Budget, na.rm =TRUE), max_grant =max(Budget, na.rm =TRUE), .groups ="drop")%>%filter(n_years==3)%>%left_join(ukr25_partner, by ="OrganizationName")%>%arrange(desc(total))%>%mutate( Type =case_when(OrganizationType=="International NGO"~"INGO",OrganizationType=="National NGO"~"NNGO",OrganizationType=="UN Agency"~"UN",TRUE~"Other"), `Total 22–24` =scales::dollar(total/1e6, suffix ="M", accuracy =0.1), `Mean grant` =scales::dollar(mean_grant/1e6, suffix ="M", accuracy =0.01), `Max grant` =scales::dollar(max_grant/1e6, suffix ="M", accuracy =0.01), `2025 total` =ifelse(is.na(amt25), "—",scales::dollar(amt25/1e6, suffix ="M", accuracy =0.01)))%>%select(Organisation =OrganizationName, Type, Grants =n_grants, `Total 22–24`, `Mean grant`, `Max grant`, `2025 total`)knitr::kable(core20, caption ="Core-20: organisations active in every UHF allocation round 2022–2024, ranked by cumulative disbursement. 2025 column shows whether the partner continued into the current cycle.", align =c("l","l","c","r","r","r","r"))
Core-20: organisations active in every UHF allocation round 2022–2024, ranked by cumulative disbursement. 2025 column shows whether the partner continued into the current cycle.
Organisation
Type
Grants
Total 22–24
Mean grant
Max grant
2025 total
Agency for Technical Cooperation and Development
INGO
8
$36.4M
$4.55M
$6.30M
$1.00M
United Nations High Commissioner for Refugees
UN
7
$33.3M
$4.76M
$9.43M
—
Danish Refugee Council
INGO
8
$30.1M
$3.76M
$5.00M
$17.00M
People in Need
INGO
6
$26.3M
$4.38M
$6.14M
$4.81M
ICF Caritas Ukraine
NNGO
7
$23.8M
$3.40M
$6.00M
$14.06M
Norwegian Refugee Council
INGO
5
$18.3M
$3.66M
$7.50M
$2.40M
Proliska
NNGO
4
$17.5M
$4.38M
$5.23M
$9.17M
Charity Foundation “NEW WAY”
NNGO
8
$12.6M
$1.58M
$3.42M
$4.64M
Charitable Organization ‘Charity Fund ’POSMISHKA UA’
NNGO
7
$12.4M
$1.76M
$2.50M
$8.50M
International Rescue Committee, Inc.
INGO
5
$11.4M
$2.28M
$4.84M
$4.25M
Stichting ZOA
INGO
5
$11.4M
$2.28M
$4.00M
$4.94M
Dorcas Aid International Transcarpathia
NNGO
6
$10.6M
$1.76M
$2.50M
$2.01M
Estonian Refugee Council
INGO
7
$9.4M
$1.35M
$2.50M
$1.50M
CORE Community Organized Relief Effort
INGO
4
$8.8M
$2.21M
$3.16M
—
Zaporizhzhia Charitable Foundation ‘Unity’ for the Future’
NNGO
4
$8.3M
$2.09M
$2.50M
$5.96M
Save the Children Fund
INGO
5
$8.2M
$1.64M
$2.50M
$2.29M
INTERSOS
INGO
6
$7.6M
$1.26M
$2.31M
$2.00M
CHARITABLE FOUNDATION ‘HUMANITARIAN AID AND DEVELOPMENT CENTRE’
NNGO
8
$6.1M
$0.76M
$1.00M
$2.00M
AVSI Foundation
INGO
4
$3.4M
$0.84M
$1.00M
$1.00M
Nonviolent Peaceforce International
INGO
3
$3.3M
$1.11M
$1.97M
$2.21M
7.4.1 Partner Distribution by Type
The four-type classification reveals how the 94 distinct Ukraine partners distribute across risk profiles:
Show code
typology_table<-typology_summary%>%arrange(desc(total_usd))%>%transmute( Type =Type_Label, `# Partners` =n_partners, `# Grants` =n_grants, `Total funding` =scales::dollar(total_usd/1e6, suffix ="M", accuracy =1), `% of envelope` =sprintf("%.1f%%", pct_of_total), `Avg partner` =scales::dollar(mean_partner_usd/1e6, suffix ="M", accuracy =0.01), `Avg grant` =scales::dollar(mean_grant/1e6, suffix ="M", accuracy =0.01))knitr::kable(typology_table, caption ="UHF partners 2022–2025 by typology. Type D (hyperscalers) dominate funding with minimal partner count; Type C (localisation cohort) balances volume with partner diversity; Types B and A remain numerically significant but fiscally secondary.", align =c("l", "c", "c", "r", "r", "r", "r"))
UHF partners 2022–2025 by typology. Type D (hyperscalers) dominate funding with minimal partner count; Type C (localisation cohort) balances volume with partner diversity; Types B and A remain numerically significant but fiscally secondary.
Type
# Partners
# Grants
Total funding
% of envelope
Avg partner
Avg grant
Small-base pre-war
84
260
$425M
57.3%
$424.80M
$1.63M
Hyperscaler
14
72
$253M
34.0%
$252.54M
$3.51M
Localisation cohort
2
16
$65M
8.7%
$64.55M
$4.03M
Hyperscalers (Type D) dominate by funding but are sparse by partner count. The localisation cohort (Type C) — pre-2022 Ukrainian NGOs — combine meaningful volume with deep field presence. Types B (small-base pre-war) and A (born of the war) are numerically larger but absorb far less funding, reflecting deliberate fund architecture: concentrate large grants in absorptive-capacity partners; use smaller allocations to maintain ecosystem diversity and test new capabilities.
Show code
# Data prep for dual visualizationtyp_budget<-typology_summary%>%arrange(Type)%>%mutate(Type_Label =factor(Type_Label, levels =c("Hyperscaler", "Localisation cohort", "Small-base pre-war", "Post-invasion entrant")))# Left: Budget by typep_budget<-ggplot(typ_budget, aes(x =Type_Label, y =total_usd/1e6, fill =Type))+geom_col(alpha =0.85, width =0.6)+geom_text(aes(label =sprintf("$%.0fM\n(%.1f%%)", total_usd/1e6, pct_of_total)), vjust =-0.3, size =3.5, fontface ="bold")+scale_fill_manual(values =c("D"="#005f73", "C"="#0a9396", "B"="#ee9b00", "A"="#ca6702"), guide ="none")+scale_y_continuous(labels =scales::dollar_format(suffix ="M", accuracy =1), expand =expansion(mult =c(0, 0.2)))+labs(x =NULL, y ="Total funding 2022–2025", title ="Budget by Partner Type")+theme_minimal(base_size =11)+theme( plot.title =element_text(face ="bold", size =12, color ="#005f73"), axis.text.x =element_text(angle =20, hjust =1, size =10), panel.grid.major.x =element_blank())# Right: Partner count by typep_partners<-ggplot(typ_budget, aes(x =Type_Label, y =n_partners, fill =Type))+geom_col(alpha =0.85, width =0.6)+geom_text(aes(label =n_partners), vjust =-0.3, size =3.5, fontface ="bold")+scale_fill_manual(values =c("D"="#005f73", "C"="#0a9396", "B"="#ee9b00", "A"="#ca6702"), guide ="none")+scale_y_continuous(expand =expansion(mult =c(0, 0.15)))+labs(x =NULL, y ="Number of partners", title ="Partner Count by Type")+theme_minimal(base_size =11)+theme( plot.title =element_text(face ="bold", size =12, color ="#005f73"), axis.text.x =element_text(angle =20, hjust =1, size =10), panel.grid.major.x =element_blank())p_budget+p_partners
The inverse relationship: Type D (hyperscalers) = smallest number, largest budget. Type A (born of the war) = rarest, smallest budgets. Type B (small-base pre-war) = larger number with moderate budgets. This pattern reflects deliberate fund architecture: concentrate delivery in proven-capacity partners, maintain ecosystem diversity via Type B and C, and use Type A selectively to pilot emerging capabilities with robust monitoring.
Seven partners are “cap-binders”: ACTED, UNHCR, DRC, People in Need, ICF Caritas Ukraine, NRC and Proliska all average grants of $3.4M–$4.8M — every grant at or near the $5M ceiling. Six of them received at least one exceptional-cap grant ≥ $6M (UNHCR $9.4M, NRC $7.5M, ACTED $6.3M, PIN $6.1M, Caritas $6.0M, Proliska $5.2M). This is where the “repetition at large scale” pattern concentrates — not across the fund, but in a ~7-organisation inner tier.
The UN exit is visible at partner level. UNHCR was the second-largest UHF partner 2022–2024 ($33.3M across 7 grants) and received $0 in 2025. CORE Community Organized Relief Effort similarly drops out. Their volume did not leave Ukraine — it was absorbed primarily by DRC ($17.0M in 2025), ICF Caritas Ukraine ($14.1M in 2025) and a broadened NNGO tier.
7.5 Dependency Ratio: When UHF Dwarfs the Partner
For the 12 core-20 members with publicly available 2021 revenue, the dependency ratio (UHF 2022–2024 volume / 2021 global organisational revenue) distinguishes absorbable UHF engagement from structural dependency.
Show code
# Revenue data compiled from publicly available sources (audited accounts# and annual reports). Unverifiable cases excluded from this table.rev_usd<-tibble::tribble(~Organisation, ~rev_2021_usd_m, ~rev_note,"Agency for Technical Cooperation and Development", 463, "€393.7M (2021)","United Nations High Commissioner for Refugees", 5150, "$5.15B (2021)","Danish Refugee Council", 495, "3.12B DKK (2021)","People in Need", 133, "€113M (2021)","Norwegian Refugee Council", 660, "5.67B NOK (2021)","International Rescue Committee, Inc.", 1160, "$1.16B (2021)","Stichting ZOA", 96, "€82M (2021)","Dorcas Aid International Transcarpathia", 28, "€24M parent (2021)","Estonian Refugee Council", 2.2, "€1.9M (2021)","CORE Community Organized Relief Effort", 122, "$122.1M (2021)","Save the Children Fund", 2200, "$2.2B (2021)","INTERSOS", 126, "€107M (2021)")dep<-ukr22_24%>%group_by(OrganizationName)%>%summarise(uhf_total =sum(Budget, na.rm =TRUE), .groups ="drop")%>%inner_join(rev_usd, by =c("OrganizationName"="Organisation"))%>%mutate( uhf_m =uhf_total/1e6, ratio =uhf_m/rev_2021_usd_m, Reading =case_when(ratio<0.1~"Minor line",ratio<0.5~"Meaningful",ratio<2~"Structural dependency",TRUE~"UHF dwarfs org"), Reading =factor(Reading, levels =c("Minor line","Meaningful","Structural dependency","UHF dwarfs org")))%>%arrange(desc(ratio))dep_tbl<-dep%>%transmute( Organisation =OrganizationName, `2021 revenue` =rev_note, `UHF 22–24` =scales::dollar(uhf_m, suffix ="M", accuracy =0.01), Ratio =sprintf("%.2f×", ratio),Reading)knitr::kable(dep_tbl, caption ="Dependency ratio for core-20 members with public 2021 revenue. Ratio = UHF 2022–2024 disbursement divided by the organisation's 2021 global revenue (USD-equivalent).", align =c("l","l","r","r","l"))
Dependency ratio for core-20 members with public 2021 revenue. Ratio = UHF 2022–2024 disbursement divided by the organisation’s 2021 global revenue (USD-equivalent).
Organisation
2021 revenue
UHF 22–24
Ratio
Reading
Estonian Refugee Council
€1.9M (2021)
$9.43M
4.29×
UHF dwarfs org
Dorcas Aid International Transcarpathia
€24M parent (2021)
$10.55M
0.38×
Meaningful
People in Need
€113M (2021)
$26.30M
0.20×
Meaningful
Stichting ZOA
€82M (2021)
$11.39M
0.12×
Meaningful
Agency for Technical Cooperation and Development
€393.7M (2021)
$36.40M
0.08×
Minor line
CORE Community Organized Relief Effort
$122.1M (2021)
$8.84M
0.07×
Minor line
Danish Refugee Council
3.12B DKK (2021)
$30.10M
0.06×
Minor line
INTERSOS
€107M (2021)
$7.58M
0.06×
Minor line
Norwegian Refugee Council
5.67B NOK (2021)
$18.29M
0.03×
Minor line
International Rescue Committee, Inc.
$1.16B (2021)
$11.42M
0.01×
Minor line
United Nations High Commissioner for Refugees
$5.15B (2021)
$33.31M
0.01×
Minor line
Save the Children Fund
$2.2B (2021)
$8.21M
0.00×
Minor line
Show code
dep_plot<-dep%>%mutate(OrganizationName =forcats::fct_reorder(OrganizationName, ratio))ggplot(dep_plot,aes(x =ratio, y =OrganizationName, colour =Reading))+geom_vline(xintercept =1, colour ="#e63946", linetype ="dashed", linewidth =0.8)+geom_segment(aes(x =0.001, xend =ratio, yend =OrganizationName), linewidth =0.4, colour ="grey70")+geom_point(size =4)+geom_text(aes(label =sprintf("%.2f×", ratio)), hjust =-0.25, size =3.2, fontface ="bold", show.legend =FALSE)+scale_x_log10( breaks =c(0.01, 0.1, 0.5, 1, 4), labels =c("0.01×", "0.1×", "0.5×", "1×", "4×"), expand =expansion(mult =c(0.05, 0.35)))+scale_colour_manual(values =c("Minor line"="#94d2bd","Meaningful"="#0a9396","Structural dependency"="#ee9b00","UHF dwarfs org"="#e63946"), name =NULL)+labs( title ="UHF dependency ratio: UHF 2022–2024 volume ÷ 2021 global revenue", subtitle ="Log scale. Red dashed line = UHF equal to entire pre-war annual revenue.", x ="Dependency ratio (log scale)", y =NULL, caption ="Source: organisation audited accounts and annual reports, 2021. UHF volume from CBPF ProjectSummary, Feb 2026.")+theme_minimal(base_size =12)+theme( plot.title =element_text(face ="bold", size =13, colour ="#005f73"), plot.subtitle =element_text(size =10, colour ="#555555"), legend.position ="top", panel.grid.minor =element_blank())
Dependency ratio by core-20 partner (log-scaled). The y=1 line marks parity between UHF volume and pre-war annual revenue. Estonian Refugee Council sits more than 4× above parity — UHF volume exceeds its entire pre-war institutional scale.
Estonian Refugee Council is the sharpest visible case, but this is only due to its financial transparency: a domestic Estonian NGO with €1.9M 2021 revenue absorbing $9.4M of UHF funding over 2022–2024 — 4.3× its pre-war annual scale. Whatever governance, finance and compliance infrastructure existed in 2021 was sized for a €1.9M/year operation. UHF is not supplementing this organisation; UHF is its operational scale. Dorcas Aid International Transcarpathia is a parallel case at 0.38× against the €24M global parent, though the country-unit share is likely higher.
Transparency gap. Seven of the eight core-20 Ukrainian NNGOs (ICF Caritas Ukraine, Proliska, NEW WAY, POSMISHKA UA, Unity for the Future, HADC, and others appearing in adjacent cohorts) have no publicly available 2021 revenue figure. An independent capacity-to-volume assessment is therefore blocked on the denominator side. This is not a criticism — it reflects a legitimate gap in the public-disclosure practice of domestic NGOs operating in a war economy — but it should be flagged in any external review of UHF’s fiduciary exposure.
8 The Ideal UHF Candidate Profile
The preceding chapters characterised the UHF system-wide. This chapter flips the lens: given what we know about how the UHF allocates money, what does an ideal partner look like? Rather than inferring intent from eligibility documents, we read the profile off the disbursement history itself — i.e. the organisations that the UHF has repeatedly trusted with large, complex programmes. Two dimensions define the profile:
Geographic coverage — the number of distinct oblasts an organisation operates in. A broad footprint signals the ability to deliver across the contact line and in liberated/recovery regions alike.
Sector coverage — the number of distinct clusters an organisation engages in. Multi-sector capacity signals the ability to bundle protection, shelter, WASH, health or food security into one integrated programme.
Together these two axes define a simple 2×2 that partitions the partner universe into Specialists, Area generalists, Sector generalists, and Full-coverage partners. The ideal candidate sits in the upper-right quadrant.
8.1 Mapping the Partner Universe
The scatter plot below places every Ukraine UHF partner (2019–2025) on the two coverage axes. Bubble size is proportional to cumulative disbursed budget; colour reflects organisation type. Dashed lines mark the 75th-percentile thresholds that split the plane into four quadrants.
UHF partner universe, 2019–2025. Oblast coverage vs cluster coverage; bubble size = total disbursed budget. Dashed lines mark the 75th-percentile thresholds. Full-coverage quadrant (upper-right) concentrates the largest, most trusted partners.
17 of 104 partners (16%) sit in the Full-coverage quadrant, but together they captured 46% of all UHF disbursements since 2019. Their median cumulative budget is US$20.9M, versus US$2.2M for Specialists — an order-of-magnitude gap that confirms coverage is the single strongest predictor of scale at the UHF.
8.2 Quadrant Breakdown by Organisation Type
Does the ideal-candidate profile favour a specific organisation type? The table below decomposes each quadrant by INGO / NNGO / UN Agency, showing both the partner count and the share of total budget that flowed to each cell.
Show code
q_tbl<-org_cov%>%group_by(Quadrant, OrgType)%>%summarise( n_orgs =n(), total_budget =sum(total_budget, na.rm =TRUE), .groups ="drop")%>%mutate( pct_bud =100*total_budget/sum(total_budget), cell =sprintf("%d orgs — US$%.1fM (%.1f%%)",n_orgs, total_budget/1e6, pct_bud))%>%select(Quadrant, OrgType, cell)%>%tidyr::pivot_wider(names_from =OrgType, values_from =cell, values_fill ="—")%>%mutate(Quadrant =factor(Quadrant, levels =c("Full-coverage", "Sector generalist","Area generalist", "Specialist")))%>%arrange(Quadrant)knitr::kable(q_tbl, caption ="Partner count and share of total disbursed budget by quadrant and organisation type (Ukraine UHF, 2019–2025)", col.names =c("Quadrant", "INGO", "NNGO", "UN Agency"))
Partner count and share of total disbursed budget by quadrant and organisation type (Ukraine UHF, 2019–2025)
Quadrant
INGO
NNGO
UN Agency
Full-coverage
8 orgs — US$174.3M (22.6%)
7 orgs — US$145.0M (18.8%)
2 orgs — US$37.1M (4.8%)
Sector generalist
10 orgs — US$47.2M (6.1%)
11 orgs — US$79.2M (10.3%)
1 orgs — US$7.5M (1.0%)
Area generalist
5 orgs — US$32.6M (4.2%)
3 orgs — US$18.6M (2.4%)
4 orgs — US$54.6M (7.1%)
Specialist
29 orgs — US$114.0M (14.8%)
23 orgs — US$42.3M (5.5%)
1 orgs — US$18.0M (2.3%)
Three readings emerge from the quadrant table:
The Full-coverage quadrant is mixed — it is not exclusively INGO territory. NNGOs and UN agencies both appear in meaningful numbers, confirming that broad footprint is achievable across organisation types. It is important to point that, in the case of Multipurpose Cash Asssistance (MPCA), UN agencies relied heavily on online registration, which means that no physical presence was actually achieved.
UN agencies cluster as Area generalists: they cover many oblasts but relatively few clusters (they tend to specialise by mandate — UNHCR on protection/shelter, IOM on mobility, FAO on food security).
NNGOs split in two: a small cohort joins the Full-coverage quadrant (e.g. ICF Caritas Ukraine, Proliska, Charity Fund “New Way”), while most remain Specialists with 1–2 oblasts and 1–3 clusters — a realistic starting point for first-time UHF applicants.
8.3 The Full-Coverage Cohort: Named List
Finally, the organisations that currently meet the ideal profile. This is the disbursement-based short-list: partners the UHF has already validated at scale on both coverage axes.
Full-coverage cohort — 17 organisations in the upper-right quadrant (Ukraine UHF, 2019–2025), ranked by total disbursed budget
Organisation
Type
Oblasts
Clusters
Years active
Projects
Total disbursed
Median grant
Danish Refugee Council
INGO
19
7
5
11
$47.4M
$4.7M
ICF Caritas Ukraine
NNGO
17
8
7
13
$39.0M
$3.5M
Agency for Technical Cooperation and Development
INGO
24
7
5
11
$38.8M
$4.9M
People in Need
INGO
15
4
7
11
$32.9M
$3.1M
Charitable Organization ‘Charitable Fund ’The Right to Protection’
NNGO
17
5
4
8
$31.9M
$4.5M
Proliska
NNGO
14
8
7
11
$28.9M
$2.7M
International Organization for Migration
UN Agency
17
5
4
8
$28.1M
$4.0M
Norwegian Refugee Council
INGO
24
7
6
8
$23.6M
$2.1M
Charitable Organization ‘Charity Fund ’POSMISHKA UA’
NNGO
13
7
4
11
$20.9M
$2.4M
Charitable organization “Charitable foundation “ROKADA”
NNGO
13
5
3
6
$12.7M
$2.4M
Save the Children Fund
INGO
9
8
7
12
$12.5M
$0.7M
United Nations Children’s Fund
UN Agency
19
5
3
5
$9.0M
$0.5M
Polish Humanitarian Action
INGO
9
7
5
8
$8.6M
$1.1M
CHARITABLE ORGANIZATION “CHARITY FOUNDATION “EAST-SOS”
NNGO
12
5
2
4
$8.1M
$1.9M
Triangle Generation Humanitaire
INGO
12
5
5
5
$6.0M
$1.0M
Terre des hommes - Aide a l’enfance dans le monde - Fondation
INGO
10
4
3
3
$4.5M
$1.7M
Ukrainian Deminers Association
NNGO
21
5
3
5
$3.5M
$0.7M
Implications for partner development. If the UHF wishes to expand this cohort — particularly by growing the NNGO pipeline — investment should target the adjacent quadrants: Area generalists (broad footprint, narrow sector) need capacity-building to add clusters; Sector generalists (multi-sector, narrow footprint) need support to expand geographically, often via consortium or sub-granting arrangements. Specialists are where the NNGO on-ramp begins.
9 Health Funding Recipients in Ukraine (2019–2025)
Since the Ukraine Humanitarian Fund was established in 2019, the Health cluster has received a significant share of total allocations. This section identifies every organisation that has received health-earmarked funding and tracks how the recipient landscape has evolved over time.
9.1 All-time Health Funding by Recipient
Show code
top_n_show<-nrow(ukr_health_orgs)# show all recipientsplot_df<-ukr_health_orgs%>%slice_head(n =top_n_show)%>%mutate( OrganizationName =stringr::str_trunc(OrganizationName, width =45), OrganizationName =factor(OrganizationName, levels =rev(unique(OrganizationName))))ggplot(plot_df,aes(x =OrganizationName, y =TotalHealth_M, colour =OrgType))+geom_segment(aes(xend =OrganizationName, y =0, yend =TotalHealth_M), linewidth =0.8, alpha =0.6)+geom_point(aes(size =n_grants), alpha =0.9)+geom_text(aes(label =paste0("$", round(TotalHealth_M, 1), "M")), hjust =-0.2, size =3.0, fontface ="bold")+coord_flip()+scale_colour_manual(values =orgtype_colors, name ="Org. type")+scale_size_continuous(name ="No. of grants", range =c(3, 9), breaks =c(1, 3, 5, 8))+scale_y_continuous( labels =scales::dollar_format(suffix ="M", accuracy =0.1), expand =expansion(mult =c(0, 0.28)))+labs( title ="Ukraine UHF: Health Funding Recipients (2019–2025)", subtitle ="All organisations that received Health-cluster funding since the fund was established.\nDot size = number of grants received.", x =NULL, y ="Total health-earmarked funding (USD millions)", caption ="Source: CBPF Cluster + ProjectSummary datasets, Feb 2026 extract")+theme_minimal(base_size =12)+theme( plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555"), axis.text.y =element_text(size =9.5), legend.position ="right", panel.grid.major.y =element_blank(), panel.grid.minor =element_blank())
39 organisations have received Health-cluster funding from the Ukraine Humanitarian Fund since 2019, together accounting for $62.4M in health-earmarked allocations. The top recipient — World Health Organization — captured 16.7% of all health funding, and the top three organisations together account for 37.5%. The recipient pool comprises 4 UN agencies, 22 INGOs and 13 NNGOs, reflecting the mixed multi-channel delivery model used in the Ukraine response.
9.2 Health Funding by Organisation Over Time
Show code
# Re-order orgs by total funding for consistent axisorg_order_health<-ukr_health_orgs%>%arrange(TotalHealth_M)%>%pull(OrganizationName)heatmap_df<-ukr_health_yearly%>%mutate( OrganizationName =stringr::str_trunc(OrganizationName, width =45), OrganizationName =factor(OrganizationName, levels =stringr::str_trunc(org_order_health, width =45)))%>%complete(OrganizationName, AllocationYear =2019:2025, fill =list(Budget_M =NA))ggplot(heatmap_df,aes(x =as.factor(AllocationYear), y =OrganizationName, fill =Budget_M))+geom_tile(colour ="white", linewidth =0.4)+geom_text(aes(label =ifelse(!is.na(Budget_M),paste0("$", round(Budget_M, 1), "M"),"")), size =2.8, colour ="white", fontface ="bold")+scale_fill_gradient( low ="#94d2bd", high ="#005f73", na.value ="grey93", name ="Health budget\n(USD millions)", labels =scales::dollar_format(suffix ="M", accuracy =0.1))+labs( title ="Ukraine UHF: Health Funding Per Recipient and Year (2019–2025)", subtitle ="Grey cells = no health-cluster grant in that year", x ="Allocation Year", y =NULL, caption ="Source: CBPF Cluster + ProjectSummary datasets, Feb 2026 extract")+theme_minimal(base_size =11)+theme( plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555"), axis.text.y =element_text(size =9), axis.text.x =element_text(size =10, face ="bold"), panel.grid =element_blank(), legend.position ="right")
The heatmap makes the temporal fragmentation of health delivery visible: organisations that carried health programming before the full-scale invasion are largely replaced post-2022 by a different set of partners with the absorptive capacity to deploy larger grants. This matters for continuity: UHF health funding is now concentrated in a handful of INGOs and a growing NNGO cohort, with no resident UN health convenor inside the fund. Monitoring whether this reconfiguration preserves technical standards, coordination, and last-mile reach will be an important diagnostic for the fund’s health portfolio going forward.
It is clear that the UHF’s health funding strategy is based on absorption rather than field experience or technical expertise. The top health recipients are not specialised health actors but rather the largest, most trusted partners with the capacity to deploy large grants at speed. Moreover, the technical miscalculation of not considering Local Health facilities as partnership candidates makes this funding impact even more questioable: https://baena.blog/00.-STOCK/The-Mistake-of-Excluding-Local-Health-Facilities-from-Localization-Targets-in-Ukraine
Furthermore, assessing the true cost-effectiveness of concentrating funding among a few ‘official’ partners remains impossible, given that 5W (Who, What, Where, When, and for Whom) operational data is frequently partial or unpublished. Despite these data constraints, a distinct profile emerges for the preferred health partners: they are predominantly Anglo-Saxon organizations—including some headquartered in non-WHO member states—that possessed no operational footprint in Ukraine prior to the 2022 escalation.
10 Cross-Check: UHF Annual Reports vs. Data
A dataset-based analysis is only useful if it agrees with — or carefully disagrees with — the fund’s own published narrative. This chapter reconciles the figures in this report against the UHF Annual Reports for 2022, 2023 and 2024. The exercise serves three purposes: (i) validate the data pipeline; (ii) surface reporting inconsistencies the reader should know about; (iii) highlight structural shifts the ARs document that are easy to miss in a year-by-year view.
Reconciliation: raw CBPF extract (Feb 2026) against UHF Annual Report headline figures.
Year
Metric
Raw data
AR figure
Verdict
2022
Total allocations
$192.7M / 56 partners / 110 projects
$192M / 56 partners / 109 projects
Match (1-project delta)
2022
Org-type split
INGO $82M · NNGO $45M · UN $66M
INGO $82M · UN $66M · NNGO $44M (direct)
Match
2023
Total allocations
$183.7M / 49 partners / 74 projects
$181.2M / 49 partners
Close (+$2.5M)
2023
Org-type split
INGO $104.0M · NNGO $38.3M · UN $41.4M
INGO $92M · NNGO $56.8M · UN $32.4M
Mismatch — see below
2024
Total allocations
$164.3M / 58 partners / 73 projects
$164.3M / 58 partners / 73 projects
Exact match
2024
Org-type split
INGO $84.6M · NNGO $74.8M · UN $5.0M
INGO $84.6M (51.9%) · NNGO $73.3M (45%) · UN $5M
Match
The 2023 org-type breakdown does not reconcile. The 2023 AR reports $56.8M to NNGOs (≈30% of the envelope); the raw CBPF extract shows $38.3M direct to National NGO entities. The most plausible reconciliation is that the AR definition includes sub-granted funding flowing from INGO prime recipients to Ukrainian NNGO sub-implementers, and possibly bundles Red Cross / partially-national entities into the NNGO category. When quoting either figure in discussion with external audiences, it is advisable to cite the definition used (direct-partner classification vs end-beneficiary-entity classification).
10.2 Structural Shifts the ARs Document
Three cross-year trends in the ARs are not obvious from any single year of data and deserve explicit highlighting.
10.2.1 1. The UN Agency exit
Show code
un_exit<-project_summary%>%filter(PooledFundName=="Ukraine",AllocationYear%in%2022:2025,!is.na(Budget), Budget>0)%>%mutate(OT =case_when(OrganizationType=="International NGO"~"INGO",OrganizationType=="National NGO"~"NNGO",OrganizationType=="UN Agency"~"UN Agency",TRUE~"Other"))%>%group_by(AllocationYear, OT)%>%summarise(amt_m =sum(Budget)/1e6, .groups ="drop")ggplot(un_exit, aes(x =AllocationYear, y =amt_m, colour =OT, group =OT))+geom_line(linewidth =1.3)+geom_point(size =3.5)+geom_text(aes(label =scales::dollar(amt_m, suffix ="M", accuracy =1)), vjust =-1.0, size =3.1, fontface ="bold", show.legend =FALSE)+scale_colour_manual(values =c("INGO"="#005f73","NNGO"="#2a9d8f","UN Agency"="#e63946","Other"="#adb5bd"), name =NULL)+scale_x_continuous(breaks =2022:2025)+scale_y_continuous(labels =scales::dollar_format(suffix ="M"), expand =expansion(mult =c(0.05, 0.15)))+labs( title ="UN Agency allocations collapsed from $66M to $5M in two years", subtitle ="Ukraine UHF, 2022–2025, USD millions by organisation type", x ="Allocation year", y ="Disbursed (USD millions)", caption ="Source: CBPF ProjectSummary dataset, Feb 2026 extract. 2024 AR: UN would no longer be funded unless absolutely necessary.")+theme_minimal(base_size =13)+theme( plot.title =element_text(face ="bold", size =14, colour ="#005f73"), plot.subtitle =element_text(size =11, colour ="#555555"), legend.position ="top", panel.grid.minor =element_blank())
The 2024 Annual Report is explicit: “UN would no longer be funded unless absolutely necessary.” The data tracks this policy: UN Agency allocations fell from $66.0M (2022) → $41.4M (2023) → $5.0M (2024) — a 92% drop in two years. Displaced volume flowed primarily to DRC, ICF Caritas Ukraine and a broadened NNGO tier (see Concentration chapter).
10.2.2 2. UHF’s growing weight in the CBPF system
Per the Annual Reports:
2023 AR: UHF = 17% of global CBPF allocations, 7% of the Ukraine HRP.
2024 AR: UHF = 20% of global CBPF allocations, >10% of the Ukraine HNRP — “largest CBPF for the third consecutive year.”
One in every five CBPF dollars globally now flows through UHF. The “7th of 28” framing used in parts of this report for reserve-allocation share is accurate for that narrow metric, but understates UHF’s overall systemic weight.
10.2.3 3. Contributions volatility with rising localisation
Headline year-on-year shifts reported in the UHF Annual Reports 2022, 2023 and 2024. WLO = Women-Led Organisation funding.
Year
Contributions (AR)
Donors (AR)
Allocations (AR)
NNGO direct (AR)
WLO funding (AR)
2022
$327M
28
$192M
33%
n/r
2023
$182M (−44% YoY)
26
$181M
30%
$9M (~5%)
2024
$232M
27
$164M
60% ($96M)
$50.4M (31%)
The 2023 contributions drop (−44% YoY) is larger than the allocations line suggests because UHF ran down prior-year cash balances. Despite partial 2024 recovery, UHF’s contribution base remains 29% below its 2022 peak — the fund’s relative systemic importance is rising on a shrinking absolute base. Meanwhile WLO-earmarked funding jumped from ≈5% to 31% of the envelope in a single year, the most dramatic single-year shift in any policy category documented in the ARs.
10.3 Allocation Cadence
All three ARs document a consistent cadence: a large early-year Reserve or Standard allocation, a mid-year Reserve, and a late-year top-up. For partners, this is the operational rhythm UHF expects them to absorb.
Show code
cadence<-tibble::tribble(~Year, ~`Round 1`, ~`Round 2`, ~`Round 3`,"2022", "RA Feb–Apr: $91M total", "RA Jul: $26M", "SA Sep: $75M (largest-ever at the time)","2023", "RA1 Jan: $52M", "SA1 Mar: $69M", "RA2 Aug: $60M","2024", "SA1 Jan: $76.2M", "RA1 Jul: $66.6M", "RA2 Oct: $21.5M","2025", "SA1 Jan: $86.8M (new record)", "—", "—")knitr::kable(cadence, caption ="UHF allocation rounds by year, as reported in the Annual Reports. 2025 reflects the January Standard Allocation documented in the 2024 AR.", align =c("l","l","l","l"))
UHF allocation rounds by year, as reported in the Annual Reports. 2025 reflects the January Standard Allocation documented in the 2024 AR.
Year
Round 1
Round 2
Round 3
2022
RA Feb–Apr: $91M total
RA Jul: $26M
SA Sep: $75M (largest-ever at the time)
2023
RA1 Jan: $52M
SA1 Mar: $69M
RA2 Aug: $60M
2024
SA1 Jan: $76.2M
RA1 Jul: $66.6M
RA2 Oct: $21.5M
2025
SA1 Jan: $86.8M (new record)
—
—
Implication for candidate partners. An organisation able to sit in the UHF’s core-20 needs to be able to respond to two Reserve triggers per calendar year and absorb a Standard allocation of $70M–$90M distributed across 30–50 partners. The cap-binding pattern documented in the Concentration chapter is the partner-level expression of this cadence.
11 Conclusions
The Ukraine Humanitarian Pooled Fund (UHF) represents a unique operational model within the global CBPF system. Driven by an unprecedented financial envelope and an aggressive localization mandate, the fund has redefined how humanitarian financing is distributed in a high-intensity, sustained conflict.
Several defining characteristics set the UHF apart from its global peers:
Scale Over Concentration: While the UHF appears highly concentrated, metric analysis (HHI, top-5 share) proves it is a broad-partnership fund. The illusion of concentration is an arithmetic byproduct of its massive envelope interacting with the standard $5M grant ceiling. Consequently, the UHF routinely issues max-sized grants to a wide array of partners, creating a flat-topped distribution unseen in any other country fund.
True Localization: Globally, International NGOs consistently receive grants 25% larger than National NGOs. In Ukraine, this paradigm has been inverted. Following the full-scale invasion—and accelerated by a deliberate reduction in UN Agency funding (falling from $66M in 2022 to $5M in 2024)—NNGOs now receive larger median grants than INGOs.
The Core-20 and Fiduciary Reality: The fund relies heavily on a stable core of 20 organizations capable of absorbing large-scale, repeated funding rounds. For several NNGOs, this has resulted in UHF volumes that dwarf their entire pre-war annual revenues. While this highlights successful capacity building, it also establishes deep structural dependencies that will require careful fiduciary and transitional management.
The “Full-Coverage” Premium: The data reveals a clear “ideal candidate” profile. Organizations that operate across multiple oblasts and sectors (Full-coverage partners) capture the vast majority of the disbursed budget. For NNGOs looking to scale, the empirical path is to evolve from geographic or sector specialists into area or sector generalists via consortiums or geographic expansion.
Health Sector Fragmentation: Despite large individual grant sizes, the health sector’s implementing landscape has experienced significant churn. The exit of historical anchor agencies like the WHO from the direct recipient pool leaves the health portfolio increasingly reliant on a shifting mix of INGOs and NNGOs, raising questions about long-term technical continuity and coordination.
Maturing Allocation Strategies: The UHF’s historical over-reliance on Reserve Allocations (51.9% globally compared to a 40.2% baseline) is beginning to self-correct. This reflects a maturation of the response: acknowledging that while humanitarian needs in Ukraine remain incredibly high, the foundational operational environment is no longer defined by the extreme volatility of 2022.
The positive: UHF has proven that it is possible to localize a massive humanitarian response without defaulting to a handful of UN mega-grants. However, sustaining this model requires ongoing vigilance regarding partner dependency, sector-specific technical leadership, and the careful balancing of emergency agility with predictable, standard funding cycles.
Despite its scale, critical transparency gaps remain within the fund. The blurred distinction between standard and reserve allocations, the absence of published pipeline data, and limited visibility into partner underspending and audit results introduce structural biases into partner selection and retention. Ultimately, this opacity risks fostering a closed ecosystem of entrenched, de facto ‘official’ partners rather than maintaining a truly competitive and evidence-based allocation process.
I specialize in task-based consulting services for data analytics. You can contact me at baena.ai/contact
Author: Jesus Baena
Report generated on 2026-04-24 using Quarto.
12 References
OCHA. (2026, February). CBPF ProjectSummary, Cluster and Location Datasets. OCHA Data Explorer. Retrieved from https://cbpfapi.unocha.org/vo1/odata
OCHA & UHF. (2023). Ukraine Humanitarian Fund 2022 Annual Report. United Nations Office for the Coordination of Humanitarian Affairs.
OCHA & UHF. (2024). Ukraine Humanitarian Fund 2023 Annual Report. United Nations Office for the Coordination of Humanitarian Affairs.
OCHA & UHF. (2025). Ukraine Humanitarian Fund 2024 Annual Report. United Nations Office for the Coordination of Humanitarian Affairs.
OCHA. (2024). CBPF Global Guidelines and Eligibility Criteria. United Nations Office for the Coordination of Humanitarian Affairs.