diff --git a/src/base/constants.jl b/src/base/constants.jl
index 257e5240877b316467bcdd4020d64f0b4a16fece..4334734e05b0deaf07efd60ca1f859d8b779fcaa 100644
--- a/src/base/constants.jl
+++ b/src/base/constants.jl
@@ -9,6 +9,9 @@ const _constants = (
     tolerance = 1e-6,
     sampling_keep_iters = 100,
     sampling_size = 1000,
+    exchange_suffixes = ["_e", "[e]", "(e)"],
+    exchange_prefixes = ["EX_", "Exch_", "Ex_", "R_EX_", "R_Ex", "R_Exch_"],
+    biomass_strings = ["BIOMASS", "biomass", "Biomass"],
     keynames = (
         rxns = ["reactions", "rxns", "RXNS", "REACTIONS", "Reactions", "Rxns"],
         mets = ["metabolites", "mets", "METS", "METABOLITES", "Metabolites", "Mets"],
diff --git a/src/base/solver.jl b/src/base/solver.jl
index 83943b844724fbd85d26bb646dadda559a4fa515..d066bf12a3d904843e309b59ad00d76a822236fc 100644
--- a/src/base/solver.jl
+++ b/src/base/solver.jl
@@ -70,3 +70,29 @@ get_bound_vectors(opt_model) = (
     [-normalized_rhs(lb) for lb in opt_model[:lbs]],
     [normalized_rhs(ub) for ub in opt_model[:ubs]],
 )
+
+"""
+    set_bound(index, optimization_model;
+        ub=_constants.default_reaction_rate,
+        lb=-_constants.default_reaction_rate)
+
+Helper function to set the bounds of variables.
+The JuMP `set_normalized_rhs` function is a little confusing, 
+so this function simplifies setting constraints. In short, JuMP
+uses a normalized right hand side representation of constraints, 
+which means that lower bounds have their sign flipped. This function
+does this for you, so you don't have to remember to do this whenever you
+change the constraints. 
+
+Just supply the constraint `index` and the JuMP model (`opt_model`) that 
+will be solved, and the variable's bounds will be set to `ub` and `lb`.
+"""
+function set_bound(
+    vind,
+    opt_model;
+    ub = _constants.default_reaction_rate,
+    lb = -_constants.default_reaction_rate,
+)
+    set_normalized_rhs(opt_model[:lbs][vind], -lb)
+    set_normalized_rhs(opt_model[:ubs][vind], ub)
+end
diff --git a/src/base/types/CoreModel.jl b/src/base/types/CoreModel.jl
index 3f6cfe07eabb2352c1164c4bb8eefffd3fbed4b5..be619c7f2bdb0c67f0177a6fd08c637e8c6886e2 100644
--- a/src/base/types/CoreModel.jl
+++ b/src/base/types/CoreModel.jl
@@ -93,6 +93,26 @@ function objective(a::CoreModel)::SparseVec
     a.c
 end
 
+"""
+    reaction_stoichiometry(model::CoreModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::CoreModel, rxn_id::String)::Dict{String, Float64}
+    Dict(m.mets[k]=>v for (k, v) in zip(findnz(m.S[:, first(indexin([rxn_id], m.rxns))])...))
+end
+
+"""
+    reaction_stoichiometry(model::CoreModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_ind` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::CoreModel, rxn_ind::Int)::Dict{String, Float64}
+    Dict(m.mets[k]=>v for (k, v) in zip(findnz(m.S[:, rxn_ind])...))
+end
+
 """
     Base.convert(::Type{CoreModel}, m::M) where {M <: MetabolicModel}
 
diff --git a/src/base/types/CoreModelCoupled.jl b/src/base/types/CoreModelCoupled.jl
index 8da3a9acf63dd27aa12c9d51ad2b2949c0a9eaed..ccffa36071ab1e84649fa795648afff28d31f43b 100644
--- a/src/base/types/CoreModelCoupled.jl
+++ b/src/base/types/CoreModelCoupled.jl
@@ -104,6 +104,26 @@ function coupling_bounds(a::CoreModelCoupled)::Tuple{SparseVec,SparseVec}
     (a.cl, a.cu)
 end
 
+"""
+    reaction_stoichiometry(model::CoreModelCoupled, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::CoreModelCoupled, rxn_id::String)::Dict{String, Float64}
+    reaction_stoichiometry(m.lm, rxn_id)
+end
+
+"""
+    reaction_stoichiometry(model::CoreModelCoupled, rxn_ind::Int)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_ind` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::CoreModelCoupled, rxn_ind::Int)::Dict{String, Float64}
+    reaction_stoichiometry(m.lm, rxn_ind)
+end
+
 """
     Base.convert(::Type{CoreModelCoupled}, mm::MetabolicModel)
 
diff --git a/src/base/types/JSONModel.jl b/src/base/types/JSONModel.jl
index 2c133c47866a03fad3406477409eb7937cef629c..48f26aebc8bcadd748e5bea5dde9fe225aa03193 100644
--- a/src/base/types/JSONModel.jl
+++ b/src/base/types/JSONModel.jl
@@ -276,6 +276,16 @@ function metabolite_notes(model::JSONModel, mid::String)::Notes
     _maybemap(_parse_notes, get(met, "notes", nothing))
 end
 
+"""
+    reaction_stoichiometry(model::JSONModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::JSONModel, rxn_id::String)::Dict{String, Float64}
+    ind = findfirst(x -> x["id"] == rxn_id, m.json["reactions"])
+    m.json["reactions"][ind]["metabolites"]
+end
 
 """
     Base.convert(::Type{JSONModel}, mm::MetabolicModel)
diff --git a/src/base/types/MATModel.jl b/src/base/types/MATModel.jl
index 110f67f1a4487f501c0e0a4fde7eab7880d20df8..95fb1b25a258e74fd3d8636cb13ec9393dcadbd2 100644
--- a/src/base/types/MATModel.jl
+++ b/src/base/types/MATModel.jl
@@ -167,6 +167,30 @@ metabolite_compartment(m::MATModel, mid::String) = _maybemap(
     get(m.mat, "metCompartment", get(m.mat, "metCompartments", nothing)),
 )
 
+
+"""
+    reaction_stoichiometry(model::MATModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::MATModel, rxn_id::String)::Dict{String, Float64}
+    rxn_ind = first(indexin([rxn_id], m.mat["rxns"]))
+    reaction_stoichiometry(m, rxn_ind)
+end
+
+"""
+    reaction_stoichiometry(model::MATModel, rxn_index)::Dict{String, Float64}
+
+Return the reaction equation of reaction with index `rxn_index` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients. Note, `rxn_index` can 
+be any suitable type that can index into an array.
+"""
+function reaction_stoichiometry(m::MATModel, rxn_ind)::Dict{String, Float64}
+    met_inds = findall(m.mat["S"][:, rxn_ind] .!= 0.0)
+    Dict(m.mat["mets"][met_ind] => m.mat["S"][met_ind, rxn_ind] for met_ind in met_inds)
+end
+
 # NOTE: There's no useful standard on how and where to store notes and
 # annotations in MATLAB models. We therefore leave it very open for the users,
 # who can easily support any annotation scheme using a custom wrapper.
diff --git a/src/base/types/SBMLModel.jl b/src/base/types/SBMLModel.jl
index 10dfb675c89fc1fd563bcc237c0976e5f60dac87..d3bdc2be48810861b2fd4c66183500e484b31dad 100644
--- a/src/base/types/SBMLModel.jl
+++ b/src/base/types/SBMLModel.jl
@@ -136,6 +136,17 @@ end
 
 _sbml_export_notes = _sbml_export_annotation
 
+
+"""
+    reaction_stoichiometry(model::SBMLModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::SBMLModel, rxn_id::String)::Dict{String, Float64}
+    m.sbml.reactions[rxn_id].stoichiometry
+end
+
 """
     Base.convert(::Type{SBMLModel}, mm::MetabolicModel)
 
diff --git a/src/base/types/StandardModel.jl b/src/base/types/StandardModel.jl
index da16c652b9feac510375d95c3095b4aeb8a41830..831c1f075465965eb17ac90660307586ddcb2edd 100644
--- a/src/base/types/StandardModel.jl
+++ b/src/base/types/StandardModel.jl
@@ -282,6 +282,16 @@ function reaction_annotations(model::StandardModel, id::String)::Maybe{Annotatio
     model.reactions[id].annotations
 end
 
+"""
+    reaction_stoichiometry(model::StandardModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::StandardModel, rxn_id::String)::Dict{String, Float64}
+    m.reactions[rxn_id].metabolites
+end
+
 """
 Base.convert(::Type{StandardModel}, model::MetabolicModel)
 
diff --git a/src/base/types/abstract/MetabolicModel.jl b/src/base/types/abstract/MetabolicModel.jl
index 163615c4cdbb42ca310b443445ec4a813edd1ffc..eebb577a54abf8679d9965be4bc24c02e61225c8 100644
--- a/src/base/types/abstract/MetabolicModel.jl
+++ b/src/base/types/abstract/MetabolicModel.jl
@@ -306,6 +306,17 @@ function reaction_subsystem(model::MetabolicModel, reaction_id::String)::Maybe{S
     return nothing
 end
 
+"""
+    reaction_stoichiometry(model::MetaboliteModel, rxn_id::String)::Dict{String, Float64}
+
+Return the reaction equation of reaction with id `rxn_id` in model. The reaction
+equation maps metabolite ids to their stoichiometric coefficients.
+"""
+function reaction_stoichiometry(m::MetabolicModel, rxn_id::String)::Dict{String, Float64}
+    mets = metabolites(m)
+    Dict(mets[k] => v for (k, v) in zip(findnz(stoichiometry(m)[:, first(indexin([rxn_id], reactions(m)))])...))
+end
+
 """
     precache!(a::MetabolicModel)::Nothing
 
diff --git a/src/base/utils/StandardModel.jl b/src/base/utils/StandardModel.jl
index 83bdb07729f8fbfd53f81ecfb00ee7e326a5dd30..780d9644ef98cd0f674d7566ebe44886429a2edf 100644
--- a/src/base/utils/StandardModel.jl
+++ b/src/base/utils/StandardModel.jl
@@ -102,29 +102,3 @@ function metabolite_fluxes(flux_dict::Dict{String,Float64}, model::StandardModel
     end
     return consuming, producing
 end
-
-"""
-    set_bound(index, optimization_model;
-        ub=_constants.default_reaction_rate,
-        lb=-_constants.default_reaction_rate)
-
-Helper function to set the bounds of variables.
-The JuMP `set_normalized_rhs` function is a little confusing, 
-so this function simplifies setting constraints. In short, JuMP
-uses a normalized right hand side representation of constraints, 
-which means that lower bounds have their sign flipped. This function
-does this for you, so you don't have to remember to do this whenever you
-change the constraints. 
-
-Just supply the constraint `index` and the JuMP model (`opt_model`) that 
-will be solved, and the variable's bounds will be set to `ub` and `lb`.
-"""
-function set_bound(
-    vind,
-    opt_model;
-    ub = _constants.default_reaction_rate,
-    lb = -_constants.default_reaction_rate,
-)
-    set_normalized_rhs(opt_model[:lbs][vind], -lb)
-    set_normalized_rhs(opt_model[:ubs][vind], ub)
-end
diff --git a/src/base/utils/looks_like.jl b/src/base/utils/looks_like.jl
new file mode 100644
index 0000000000000000000000000000000000000000..2082701e6d64dc6995ab64e5bc42bf4763e4369b
--- /dev/null
+++ b/src/base/utils/looks_like.jl
@@ -0,0 +1,82 @@
+"""
+    looks_like_exchange_reaction(rxn_id::String;
+        exclude_biomass = false,
+        biomass_strings = _constants.biomass_strings,
+        exchange_prefixes = _constants.exchange_prefixes,
+    )
+
+A predicate function that can be used to identify reactions that are probably 
+exchange or biomass reactions. Exchange reactions are identified based on matching prefixes 
+in the set `exchange_prefixes` and biomass reactions are identified by looking for occurences 
+of `biomass_strings` in the reaction id.
+
+Note: the order of the probable exchange reactions identified here does not necessarily match 
+that of the exchange metabolites identified in [`looks_like_exchange_metabolite`](@ref).
+
+# Example
+```
+filter(looks_like_exchange_reaction, reactions(model)) # returns strings
+findall(looks_like_exchange_reaction, reactions(model)) # returns indices
+
+# to use the optional arguments you need to expand the function's arguments using an anonymous function
+filter(x -> looks_like_exchange_reaction(x; exclude_biomass=true), reactions(model)) # returns strings
+findall(x -> looks_like_exchange_reaction(x; exclude_biomass=true), reactions(model)) # returns indices
+```
+"""
+function looks_like_exchange_reaction(rxn_id::String;
+    exclude_biomass = false,
+    biomass_strings = _constants.biomass_strings,
+    exchange_prefixes = _constants.exchange_prefixes,
+)::Bool
+    any(startswith(rxn_id, x) for x in exchange_prefixes) && !(exclude_biomass && any(occursin(x, rxn_id) for x in biomass_strings))
+end
+
+"""
+    looks_like_biomass_reaction(rxn_id::String;
+        exclude_exchanges = false,
+        exchange_prefixes = _constants.exchange_prefixes,
+        biomass_strings = _constants.biomass_strings,
+    )::Bool
+
+A predicate function that can be used to identify reactions that are probably 
+biomass reactions. Biomass reactions are identified by looking for occurences 
+of `biomass_strings` in the reaction id. Can also `exclude_exchanges` that will 
+remove occurrences of biomass reactions that have a prefix in `exchange_prefixes`.
+
+# Example
+```
+filter(looks_like_biomass_reaction, reactions(model)) # returns strings
+findall(looks_like_biomass_reaction, reactions(model)) # returns indices
+```
+"""
+function looks_like_biomass_reaction(rxn_id::String;
+    exclude_exchanges = false,
+    exchange_prefixes = _constants.exchange_prefixes,
+    biomass_strings = _constants.biomass_strings,
+)::Bool
+    any(occursin(x, rxn_id) for x in biomass_strings) && !(exclude_exchanges && any(startswith(rxn_id, x) for x in exchange_prefixes))
+end
+
+"""
+    looks_like_exchange_metabolite(rxn_id::String;
+        exchange_suffixes = _constants.exchange_suffixes,
+        )::Bool
+
+A predicate function that can be used to identify metabolites that are probably 
+involved in exchange reactions. Exchange metabolites are identified by looking for occurences 
+of `exchange_suffixes` at the end of the metabolite id.
+
+Note: the order of the probable exchange metabolites identified here does not necessarily match 
+that of the exchange reactions identified in [`looks_like_exchange_reaction`](@ref).
+
+# Example
+```
+filter(looks_like_exchange_metabolite, metabolites(model)) # returns strings
+findall(looks_like_exchange_metabolite, metabolites(model)) # returns indices
+```
+"""
+function looks_like_exchange_metabolite(met_id::String;
+    exchange_suffixes = _constants.exchange_suffixes,
+)::Bool
+    any(endswith(met_id, x) for x in exchange_suffixes)
+end
diff --git a/src/reconstruction/CoreModel.jl b/src/reconstruction/CoreModel.jl
index 1da4a088733f1b094cb0458fe5043655c29b76dd..083d549853ad1bbd401662c865886f96f5f63980 100644
--- a/src/reconstruction/CoreModel.jl
+++ b/src/reconstruction/CoreModel.jl
@@ -325,69 +325,6 @@ function remove_reactions(m::CoreModel, rxns::Vector{String})
     end
 end
 
-
-"""
-    find_exchange_reactions(
-        model::CoreModel;
-        exclude_biomass = false,
-        biomass_str::String = "biomass",
-        exc_prefs = ["EX_"; "Exch_"; "Ex_"],
-    )
-
-Get indices of exchange reactions.
-
-Exchange reactions are identified based on most commonly used prefixes.
-
-"""
-function find_exchange_reactions(
-    model::CoreModel;
-    exclude_biomass = false,
-    biomass_str::String = "biomass",
-    exc_prefs = ["EX_"; "Exch_"; "Ex_"],
-)
-    is_exc = falses(n_reactions(model))
-    for pref in exc_prefs
-        is_exc = is_exc .| startswith.(model.rxns, pref)
-    end
-    exc_inds = findall(is_exc)
-    if exclude_biomass
-        biom_inds = findall(x -> occursin(biomass_str, x), model.rxns)
-        exc_inds = setdiff(exc_inds, biom_inds)
-    end
-    return exc_inds
-end
-
-"""
-    find_exchange_metabolites(
-        model::CoreModel;
-        exclude_biomass = false,
-        biomass_str::String = "biomass",
-        exc_prefs = ["EX_"; "Exch_"; "Ex_"],
-    )
-
-Get indices of exchanged metabolites.
-
-In practice returns the metabolites consumed by the reactions given by `find_exchange_reactions`
-and if called with the same arguments, the two outputs correspond.
-
-"""
-function find_exchange_metabolites(
-    model::CoreModel;
-    exclude_biomass = false,
-    biomass_str::String = "biomass",
-    exc_prefs = ["EX_"; "Exch_"; "Ex_"],
-)
-    exc_rxn_inds = find_exchange_reactions(
-        model,
-        exclude_biomass = exclude_biomass,
-        biomass_str = biomass_str,
-        exc_prefs = exc_prefs,
-    )
-    exc_met_inds = [findfirst(x -> x == -1, model.S[:, j]) for j in exc_rxn_inds]
-    return exc_met_inds
-end
-
-
 """
     change_bounds!(
         model::CoreModel,
diff --git a/test/base/types/CoreModel.jl b/test/base/types/CoreModel.jl
new file mode 100644
index 0000000000000000000000000000000000000000..d1d88e9679927bd7087d5ae6685d1779cc371ca9
--- /dev/null
+++ b/test/base/types/CoreModel.jl
@@ -0,0 +1,6 @@
+@testset "CoreModel generic interface" begin
+    model = load_model(CoreModel, model_paths["e_coli_core.mat"])
+
+    @test reaction_stoichiometry(model, "EX_ac_e") == Dict("ac_e" => -1)
+    @test reaction_stoichiometry(model, 44) == Dict("ac_e" => -1)
+end
diff --git a/test/base/types/CoreModelCoupled.jl b/test/base/types/CoreModelCoupled.jl
new file mode 100644
index 0000000000000000000000000000000000000000..aa37b66a95d6d7522a3d36efb42cc8f3c7875e88
--- /dev/null
+++ b/test/base/types/CoreModelCoupled.jl
@@ -0,0 +1,7 @@
+@testset "CoreModelCoupled generic interface" begin
+    model = load_model(CoreModelCoupled, model_paths["e_coli_core.mat"])
+
+    @test reaction_stoichiometry(model, "EX_ac_e") == Dict("ac_e" => -1)
+    @test reaction_stoichiometry(model, 44) == Dict("ac_e" => -1)
+
+end
diff --git a/test/base/types/JSONModel.jl b/test/base/types/JSONModel.jl
index 428475c93aa170d74064cc0b915c062ceeb78817..3c63a68957e9bc355b124d7b17f529082541d561 100644
--- a/test/base/types/JSONModel.jl
+++ b/test/base/types/JSONModel.jl
@@ -8,3 +8,9 @@
     @test Set(reactions(jm)) == Set(reactions(sm))
     @test Set(reactions(jm)) == Set(reactions(jm2))
 end
+
+@testset "JSONModel generic interface" begin
+    model = load_model(model_paths["e_coli_core.json"])
+
+    @test reaction_stoichiometry(model, "EX_ac_e") == Dict("ac_e" => -1)
+end
diff --git a/test/base/types/MATModel.jl b/test/base/types/MATModel.jl
index 3203bfa3b70a6f9e2fe4b4d37a287063cc5291a1..10103d8d106d748c1f3cee1ea0c2dbbaaa3028f3 100644
--- a/test/base/types/MATModel.jl
+++ b/test/base/types/MATModel.jl
@@ -9,3 +9,10 @@
     @test Set(reactions(mm)) == Set(reactions(sm))
     @test Set(reactions(mm)) == Set(reactions(mm2))
 end
+
+@testset "MATModel generic interface" begin
+    model = load_model(model_paths["e_coli_core.mat"])
+
+    @test reaction_stoichiometry(model, "EX_ac_e") == Dict("ac_e" => -1)
+    @test reaction_stoichiometry(model, 44) == Dict("ac_e" => -1)
+end
diff --git a/test/base/types/SBMLModel.jl b/test/base/types/SBMLModel.jl
index d039801e321315ec8892ee37fd28ba3e5ec875b1..f515df8c8c2996f341a5ad55eae5efdfac77f6bd 100644
--- a/test/base/types/SBMLModel.jl
+++ b/test/base/types/SBMLModel.jl
@@ -12,3 +12,9 @@
         i in reactions(sbmlm2)
     ])
 end
+
+@testset "SBMLModel generic interface" begin
+    model = load_model(model_paths["e_coli_core.xml"])
+
+    @test reaction_stoichiometry(model, "R_EX_ac_e") == Dict("M_ac_e" => -1)
+end
diff --git a/test/base/types/StandardModel.jl b/test/base/types/StandardModel.jl
index 32ec945a53ea0ef23ee67207f86bd393b10ff38a..74eafb432035a2377135ec4763820d2c37afa6aa 100644
--- a/test/base/types/StandardModel.jl
+++ b/test/base/types/StandardModel.jl
@@ -1,4 +1,4 @@
-@testset "StandardModel tests" begin
+@testset "StandardModel generic interface" begin
     # create a small model
     m1 = Metabolite("m1")
     m1.formula = "C2H3"
@@ -114,6 +114,8 @@
     @test isempty(reaction_notes(model, "r2"))
     @test isempty(reaction_annotations(model, "r2"))
 
+    @test reaction_stoichiometry(model, "r1") == Dict("m1" => -1.0, "m2" => 1.0)
+
     # To do: test convert
     same_model = convert(StandardModel, model)
     @test same_model == model
diff --git a/test/base/utils/CoreModel.jl b/test/base/utils/CoreModel.jl
index e66d874c1fb07683c311447530c1c3b3597754b6..91b4b9c66b1214b677627ad05381445696b545d1 100644
--- a/test/base/utils/CoreModel.jl
+++ b/test/base/utils/CoreModel.jl
@@ -1,4 +1,3 @@
-
 @testset "CoreModel utilities" begin
     cp = test_LP()
     @test n_reactions(cp) == 3
diff --git a/test/base/utils/looks_like.jl b/test/base/utils/looks_like.jl
new file mode 100644
index 0000000000000000000000000000000000000000..08349a3ac1282b520aeb17b39e1147a51f874deb
--- /dev/null
+++ b/test/base/utils/looks_like.jl
@@ -0,0 +1,67 @@
+@testset "Looks like in CoreModel, detailed test" begin
+    cp = test_LP()
+    @test isempty(filter(looks_like_exchange_reaction, reactions(cp)))
+
+    cp = test_simpleLP()
+    @test isempty(filter(looks_like_exchange_reaction, reactions(cp)))
+
+    cp = CoreModel(
+        [-1.0 -1 -2; 0 -1 0; 0 0 0],
+        zeros(3),
+        ones(3),
+        ones(3),
+        ones(3),
+        ["EX_m1"; "r2"; "r3"],
+        ["m1"; "m2"; "m3"],
+    )
+    @test filter(looks_like_exchange_reaction, reactions(cp)) == ["EX_m1"]
+
+    cp = CoreModel(
+        [-1.0 0 0; 0 0 -1; 0 -1 0],
+        zeros(3),
+        ones(3),
+        ones(3),
+        ones(3),
+        ["EX_m1"; "Exch_m3"; "Ex_m2"],
+        ["m1"; "m2"; "m3_e"],
+    )
+    @test filter(looks_like_exchange_reaction, reactions(cp)) == ["EX_m1", "Exch_m3", "Ex_m2"]
+    @test filter(x -> looks_like_exchange_reaction(x; exchange_prefixes = ["Exch_"]), reactions(cp)) == ["Exch_m3"]
+
+    # this is originally the "toyModel1.mat"
+    cp = test_toyModel()
+
+    @test filter(looks_like_exchange_reaction, reactions(cp)) == ["EX_m1(e)", "EX_m3(e)", "EX_biomass(e)"]
+    @test filter(x -> looks_like_exchange_reaction(x; exclude_biomass=true), reactions(cp)) == ["EX_m1(e)", "EX_m3(e)"]
+    @test filter(looks_like_exchange_metabolite, metabolites(cp)) == ["m1[e]", "m3[e]"]
+    @test filter(looks_like_biomass_reaction, reactions(cp)) == ["EX_biomass(e)", "biomass1"]
+    @test filter(x -> looks_like_biomass_reaction(x; exclude_exchanges=true), reactions(cp)) == ["biomass1"] 
+end
+
+@testset "Looks like functions, basic" begin
+    model = load_model(model_paths["e_coli_core.json"])
+    @test length(filter(looks_like_exchange_reaction, reactions(model))) == 20
+    @test length(filter(looks_like_exchange_metabolite, metabolites(model))) == 20
+    @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
+    
+    model = load_model(model_paths["e_coli_core.xml"])
+    @test length(filter(looks_like_exchange_reaction, reactions(model))) == 20
+    @test length(filter(looks_like_exchange_metabolite, metabolites(model))) == 20
+    @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
+    
+    model = load_model(model_paths["e_coli_core.mat"])
+    @test length(filter(looks_like_exchange_reaction, reactions(model))) == 20
+    @test length(filter(looks_like_exchange_metabolite, metabolites(model))) == 20
+    @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
+    
+    model = convert(StandardModel, model)
+    @test length(filter(looks_like_exchange_reaction, reactions(model))) == 20
+    @test length(filter(looks_like_exchange_metabolite, metabolites(model))) == 20
+    @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
+    
+    model = convert(CoreModelCoupled, model)
+    @test length(filter(looks_like_exchange_reaction, reactions(model))) == 20
+    @test length(filter(looks_like_exchange_metabolite, metabolites(model))) == 20
+    @test length(filter(looks_like_biomass_reaction, reactions(model))) == 1
+    
+end
diff --git a/test/data_downloaded.jl b/test/data_downloaded.jl
index 0525222aff21b2b4a3ef46191e281fb0f7173b8a..f74954a689649a8012ee9ea941912d9b48928799 100644
--- a/test/data_downloaded.jl
+++ b/test/data_downloaded.jl
@@ -48,6 +48,16 @@ model_paths = Dict{String,String}(
         df("e_coli_core.json"),
         "7bedec10576cfe935b19218dc881f3fb14f890a1871448fc19a9b4ee15b448d8",
     ),
+    "e_coli_core.xml" => download_data_file(
+        "http://bigg.ucsd.edu/static/models/e_coli_core.xml",
+        df("e_coli_core.xml"),
+        "b4db506aeed0e434c1f5f1fdd35feda0dfe5d82badcfda0e9d1342335ab31116",
+    ),
+    "e_coli_core.mat" => download_data_file(
+        "http://bigg.ucsd.edu/static/models/e_coli_core.mat",
+        df("e_coli_core.mat"),
+        "478e6fa047ede1b248975d7565208ac9363a44dd64aad1900b63127394f4175b",
+    ),
     "iJR904.mat" => download_data_file(
         "http://bigg.ucsd.edu/static/models/iJR904.mat",
         df("iJR904.mat"),
diff --git a/test/reconstruction/CoreModel.jl b/test/reconstruction/CoreModel.jl
index c51ff99b949ad3fe22407c55b40981a428873b94..fb4e499ee5b69a6e380a3fbcdb23032e03deb8ba 100644
--- a/test/reconstruction/CoreModel.jl
+++ b/test/reconstruction/CoreModel.jl
@@ -1,51 +1,3 @@
-@testset "Find exchange reactions and metabolites" begin
-    cp = test_LP()
-    @test isempty(find_exchange_reactions(cp))
-    @test isempty(find_exchange_metabolites(cp))
-
-    cp = test_simpleLP()
-    @test isempty(find_exchange_reactions(cp))
-    @test isempty(find_exchange_metabolites(cp))
-
-    cp = CoreModel(
-        [-1.0 -1 -2; 0 -1 0; 0 0 0],
-        zeros(3),
-        ones(3),
-        ones(3),
-        ones(3),
-        ["EX_m1"; "r2"; "r3"],
-        ["m1"; "m2"; "m3"],
-    )
-    @test find_exchange_reactions(cp) == [1]
-
-    cp = CoreModel(
-        [-1.0 0 0; 0 0 -1; 0 -1 0],
-        zeros(3),
-        ones(3),
-        ones(3),
-        ones(3),
-        ["EX_m1"; "Exch_m3"; "Ex_m2"],
-        ["m1"; "m2"; "m3"],
-    )
-    @test find_exchange_reactions(cp) == [1; 2; 3]
-    @test find_exchange_metabolites(cp) == [1; 3; 2]
-    @test find_exchange_reactions(cp, exc_prefs = ["Exch_"]) == [2]
-    @test find_exchange_metabolites(cp, exc_prefs = ["Exch_"]) == [3]
-
-    # this is originally the "toyModel1.mat"
-    cp = test_toyModel()
-
-    @test find_exchange_reactions(cp) == [4; 5; 6]
-    @test find_exchange_metabolites(cp) == [4; 5; 6]
-    @test find_exchange_reactions(cp, exclude_biomass = true) == [4; 5]
-    @test find_exchange_metabolites(cp, exclude_biomass = true) == [4; 5]
-    @test find_exchange_reactions(cp, exclude_biomass = true, biomass_str = "biom") ==
-          [4; 5]
-    @test find_exchange_metabolites(cp, exclude_biomass = true, biomass_str = "biom") ==
-          [4; 5]
-end
-
-
 @testset "Change bounds" begin
     cp = test_LP()
     change_bounds!(cp, [3; 1], xl = [-10.0; -20], xu = [10.0; 20])
@@ -97,8 +49,6 @@ end
     @test new_mets == [4]
 end
 
-
-
 @testset "Add reactions (checking existence and consistency)" begin
     cp = test_LP()
     @test size(cp.S) == (4, 3)
@@ -206,7 +156,6 @@ end
     )
 end
 
-
 @testset "Remove reactions" begin
     cp = test_LP()
     @test size(cp.S) == (4, 3)
diff --git a/test/reconstruction/CoreModelCoupled.jl b/test/reconstruction/CoreModelCoupled.jl
index 5a1147225400aa52d6ece4f353ecffcdda6f805a..febd16ed6f31e3f483ac38de19f08774e481ce65 100644
--- a/test/reconstruction/CoreModelCoupled.jl
+++ b/test/reconstruction/CoreModelCoupled.jl
@@ -147,39 +147,6 @@ end
     @test new_cp.C[:] == cp.C[:, 3]
 end
 
-@testset "Find exchanges" begin
-    cp = convert(
-            CoreModelCoupled,
-            CoreModel(
-                [-1.0 -1 -2; 0 -1 0; 0 0 0],
-                zeros(3),
-                ones(3),
-                ones(3),
-                ones(3),
-                ["EX_m1"; "r2"; "r3"],
-                ["m1"; "m2"; "m3"]
-            )
-        )
-    @test find_exchange_reactions(cp) == [1]
-
-    cp = convert(
-            CoreModelCoupled,
-            CoreModel(
-                [-1.0 0 0; 0 0 -1; 0 -1 0],
-                zeros(3),
-                ones(3),
-                ones(3),
-                ones(3),
-                ["EX_m1"; "Exch_m3"; "Ex_m2"],
-                ["m1"; "m2"; "m3"]
-            )
-        )
-    @test find_exchange_reactions(cp) == [1; 2; 3]
-    @test find_exchange_metabolites(cp) == [1; 3; 2]
-    @test find_exchange_reactions(cp, exc_prefs = ["Exch_"]) == [2]
-    @test find_exchange_metabolites(cp, exc_prefs = ["Exch_"]) == [3]
-end
-
 @testset "Change bounds" begin
     cp = convert(CoreModelCoupled, test_LP())
     change_bounds!(cp, [3; 1], xl = [-10.0; -20], xu = [10.0; 20])